diff --git a/README.md b/README.md index 44ffaba3..080aba48 100644 --- a/README.md +++ b/README.md @@ -65,4 +65,4 @@ Email: If you have any problem in building or running the code, please do not hesitate to contact. -Copyright 2017-2025, He Xin, and the OneFLOW contributors. +Copyright 2017-2026, He Xin, and the OneFLOW contributors. diff --git a/README_zh_CN.md b/README_zh_CN.md index 77a9115a..281d4839 100644 --- a/README_zh_CN.md +++ b/README_zh_CN.md @@ -106,4 +106,4 @@ The current OneFLOW release has been coordinated by the OneFLOW International De 如果在编译和运行代码中遇到问题,可随时通过邮件联系。 -Copyright 2017-2025, He Xin, and the OneFLOW contributors. +Copyright 2017-2026, He Xin, and the OneFLOW contributors. diff --git a/codes/adt/include/AdtTree.h b/codes/adt/include/AdtTree.h index 16167ba9..fe480d0d 100644 --- a/codes/adt/include/AdtTree.h +++ b/codes/adt/include/AdtTree.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/array/include/HXArray.h b/codes/array/include/HXArray.h index c4706dce..328f2dcc 100644 --- a/codes/array/include/HXArray.h +++ b/codes/array/include/HXArray.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/array/include/Marray.h b/codes/array/include/Marray.h index c47b72e2..a64150e0 100644 --- a/codes/array/include/Marray.h +++ b/codes/array/include/Marray.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/array/src/Marray.cpp b/codes/array/src/Marray.cpp index 48a6b430..a3ebd517 100644 --- a/codes/array/src/Marray.cpp +++ b/codes/array/src/Marray.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/basic/include/Constant.h b/codes/basic/include/Constant.h index 7c5d11ab..5ace3488 100644 --- a/codes/basic/include/Constant.h +++ b/codes/basic/include/Constant.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/basic/include/HXDefine.h b/codes/basic/include/HXDefine.h index e1058aab..0bf9e8dc 100644 --- a/codes/basic/include/HXDefine.h +++ b/codes/basic/include/HXDefine.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/basic/include/HXMid.h b/codes/basic/include/HXMid.h index 0efa7704..a69ac2cc 100644 --- a/codes/basic/include/HXMid.h +++ b/codes/basic/include/HXMid.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/basic/include/HXPointer.h b/codes/basic/include/HXPointer.h index b54f97c8..7ff81f4f 100644 --- a/codes/basic/include/HXPointer.h +++ b/codes/basic/include/HXPointer.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/basic/include/HXSort.h b/codes/basic/include/HXSort.h index ddf25e4d..52118037 100644 --- a/codes/basic/include/HXSort.h +++ b/codes/basic/include/HXSort.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/basic/include/HXStd.h b/codes/basic/include/HXStd.h index 1d15c062..7db88eed 100644 --- a/codes/basic/include/HXStd.h +++ b/codes/basic/include/HXStd.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/basic/include/HXType.h b/codes/basic/include/HXType.h index a73cfcc3..d4129276 100644 --- a/codes/basic/include/HXType.h +++ b/codes/basic/include/HXType.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/basic/include/HXTypeBasic.h b/codes/basic/include/HXTypeBasic.h index 9523e904..10c8e6e8 100644 --- a/codes/basic/include/HXTypeBasic.h +++ b/codes/basic/include/HXTypeBasic.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/basic/include/HXVector.h b/codes/basic/include/HXVector.h index dad9f8f1..f9243108 100644 --- a/codes/basic/include/HXVector.h +++ b/codes/basic/include/HXVector.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/basic/src/HXDefine.cpp b/codes/basic/src/HXDefine.cpp index 138e934e..bee347c6 100644 --- a/codes/basic/src/HXDefine.cpp +++ b/codes/basic/src/HXDefine.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/basic/src/HXMid.cpp b/codes/basic/src/HXMid.cpp index ca9761b8..92a5518d 100644 --- a/codes/basic/src/HXMid.cpp +++ b/codes/basic/src/HXMid.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/include/CgnsBase.h b/codes/cgns/include/CgnsBase.h index 107e2c14..0a70bb59 100644 --- a/codes/cgns/include/CgnsBase.h +++ b/codes/cgns/include/CgnsBase.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/include/CgnsBaseUtil.h b/codes/cgns/include/CgnsBaseUtil.h index e2a5f6c8..a9f68083 100644 --- a/codes/cgns/include/CgnsBaseUtil.h +++ b/codes/cgns/include/CgnsBaseUtil.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/include/CgnsBc1to1.h b/codes/cgns/include/CgnsBc1to1.h index 8702bd86..c455a5d5 100644 --- a/codes/cgns/include/CgnsBc1to1.h +++ b/codes/cgns/include/CgnsBc1to1.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/include/CgnsBcBoco.h b/codes/cgns/include/CgnsBcBoco.h index e38b293a..ab2a58f7 100644 --- a/codes/cgns/include/CgnsBcBoco.h +++ b/codes/cgns/include/CgnsBcBoco.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/include/CgnsBcConn.h b/codes/cgns/include/CgnsBcConn.h index 8ce5048c..9b3e45ea 100644 --- a/codes/cgns/include/CgnsBcConn.h +++ b/codes/cgns/include/CgnsBcConn.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/include/CgnsBcLink.h b/codes/cgns/include/CgnsBcLink.h index f88384fe..d0b973c0 100644 --- a/codes/cgns/include/CgnsBcLink.h +++ b/codes/cgns/include/CgnsBcLink.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/include/CgnsCoor.h b/codes/cgns/include/CgnsCoor.h index f440858c..bf3d2730 100644 --- a/codes/cgns/include/CgnsCoor.h +++ b/codes/cgns/include/CgnsCoor.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/include/CgnsFactory.h b/codes/cgns/include/CgnsFactory.h index 42023824..b4ceac99 100644 --- a/codes/cgns/include/CgnsFactory.h +++ b/codes/cgns/include/CgnsFactory.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/include/CgnsFamilyBc.h b/codes/cgns/include/CgnsFamilyBc.h index 4b9b267d..0bc845ef 100644 --- a/codes/cgns/include/CgnsFamilyBc.h +++ b/codes/cgns/include/CgnsFamilyBc.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/include/CgnsFile.h b/codes/cgns/include/CgnsFile.h index 77a425c7..adba145f 100644 --- a/codes/cgns/include/CgnsFile.h +++ b/codes/cgns/include/CgnsFile.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/include/CgnsGlobal.h b/codes/cgns/include/CgnsGlobal.h index 9ec54e4f..206eafe1 100644 --- a/codes/cgns/include/CgnsGlobal.h +++ b/codes/cgns/include/CgnsGlobal.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/include/CgnsPeriod.h b/codes/cgns/include/CgnsPeriod.h index 2bdf89fb..ffed4f77 100644 --- a/codes/cgns/include/CgnsPeriod.h +++ b/codes/cgns/include/CgnsPeriod.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/include/CgnsSection.h b/codes/cgns/include/CgnsSection.h index bc9c4352..39df28c0 100644 --- a/codes/cgns/include/CgnsSection.h +++ b/codes/cgns/include/CgnsSection.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/include/CgnsVariable.h b/codes/cgns/include/CgnsVariable.h index 6ffee78e..65369634 100644 --- a/codes/cgns/include/CgnsVariable.h +++ b/codes/cgns/include/CgnsVariable.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/include/CgnsZbase.h b/codes/cgns/include/CgnsZbase.h index e283e31b..bbc12269 100644 --- a/codes/cgns/include/CgnsZbase.h +++ b/codes/cgns/include/CgnsZbase.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/include/CgnsZbaseUtil.h b/codes/cgns/include/CgnsZbaseUtil.h index 0355e880..9cf02037 100644 --- a/codes/cgns/include/CgnsZbaseUtil.h +++ b/codes/cgns/include/CgnsZbaseUtil.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/include/CgnsZbc.h b/codes/cgns/include/CgnsZbc.h index 880604b6..b2508fb0 100644 --- a/codes/cgns/include/CgnsZbc.h +++ b/codes/cgns/include/CgnsZbc.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/include/CgnsZbc1to1.h b/codes/cgns/include/CgnsZbc1to1.h index e1331989..c2502991 100644 --- a/codes/cgns/include/CgnsZbc1to1.h +++ b/codes/cgns/include/CgnsZbc1to1.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/include/CgnsZbcBoco.h b/codes/cgns/include/CgnsZbcBoco.h index 30ec9c40..eafa4c8c 100644 --- a/codes/cgns/include/CgnsZbcBoco.h +++ b/codes/cgns/include/CgnsZbcBoco.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/include/CgnsZbcConn.h b/codes/cgns/include/CgnsZbcConn.h index 7dd9e8a2..7134d563 100644 --- a/codes/cgns/include/CgnsZbcConn.h +++ b/codes/cgns/include/CgnsZbcConn.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/include/CgnsZone.h b/codes/cgns/include/CgnsZone.h index f658aeaa..8b6ff4a8 100644 --- a/codes/cgns/include/CgnsZone.h +++ b/codes/cgns/include/CgnsZone.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/include/CgnsZoneUtil.h b/codes/cgns/include/CgnsZoneUtil.h index 41236b35..78b03d35 100644 --- a/codes/cgns/include/CgnsZoneUtil.h +++ b/codes/cgns/include/CgnsZoneUtil.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/include/CgnsZsection.h b/codes/cgns/include/CgnsZsection.h index 1d855504..bc6e39fd 100644 --- a/codes/cgns/include/CgnsZsection.h +++ b/codes/cgns/include/CgnsZsection.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/include/HXCgns.h b/codes/cgns/include/HXCgns.h index ca4d7477..54017e27 100644 --- a/codes/cgns/include/HXCgns.h +++ b/codes/cgns/include/HXCgns.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/src/CgnsBase.cpp b/codes/cgns/src/CgnsBase.cpp index c4620fc4..9c3f1a8d 100644 --- a/codes/cgns/src/CgnsBase.cpp +++ b/codes/cgns/src/CgnsBase.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/src/CgnsBaseUtil.cpp b/codes/cgns/src/CgnsBaseUtil.cpp index 80fe7667..cb0cabfd 100644 --- a/codes/cgns/src/CgnsBaseUtil.cpp +++ b/codes/cgns/src/CgnsBaseUtil.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/src/CgnsBc1to1.cpp b/codes/cgns/src/CgnsBc1to1.cpp index 1ba91573..51df5c90 100644 --- a/codes/cgns/src/CgnsBc1to1.cpp +++ b/codes/cgns/src/CgnsBc1to1.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/src/CgnsBcBoco.cpp b/codes/cgns/src/CgnsBcBoco.cpp index 6405e294..0839b341 100644 --- a/codes/cgns/src/CgnsBcBoco.cpp +++ b/codes/cgns/src/CgnsBcBoco.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/src/CgnsBcConn.cpp b/codes/cgns/src/CgnsBcConn.cpp index 6928905e..ed11e774 100644 --- a/codes/cgns/src/CgnsBcConn.cpp +++ b/codes/cgns/src/CgnsBcConn.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/src/CgnsBcLink.cpp b/codes/cgns/src/CgnsBcLink.cpp index d84245d7..5c1eccc8 100644 --- a/codes/cgns/src/CgnsBcLink.cpp +++ b/codes/cgns/src/CgnsBcLink.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/src/CgnsCoor.cpp b/codes/cgns/src/CgnsCoor.cpp index 2d448b96..4c092dbb 100644 --- a/codes/cgns/src/CgnsCoor.cpp +++ b/codes/cgns/src/CgnsCoor.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/src/CgnsFactory.cpp b/codes/cgns/src/CgnsFactory.cpp index 6927feca..aa0a6e05 100644 --- a/codes/cgns/src/CgnsFactory.cpp +++ b/codes/cgns/src/CgnsFactory.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/src/CgnsFamilyBc.cpp b/codes/cgns/src/CgnsFamilyBc.cpp index 975a49b3..85c8b365 100644 --- a/codes/cgns/src/CgnsFamilyBc.cpp +++ b/codes/cgns/src/CgnsFamilyBc.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/src/CgnsFile.cpp b/codes/cgns/src/CgnsFile.cpp index 4643e26f..667d74cd 100644 --- a/codes/cgns/src/CgnsFile.cpp +++ b/codes/cgns/src/CgnsFile.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/src/CgnsGlobal.cpp b/codes/cgns/src/CgnsGlobal.cpp index 89e26ddc..8fe422fe 100644 --- a/codes/cgns/src/CgnsGlobal.cpp +++ b/codes/cgns/src/CgnsGlobal.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/src/CgnsPeriod.cpp b/codes/cgns/src/CgnsPeriod.cpp index 23603a79..70815c9e 100644 --- a/codes/cgns/src/CgnsPeriod.cpp +++ b/codes/cgns/src/CgnsPeriod.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/src/CgnsSection.cpp b/codes/cgns/src/CgnsSection.cpp index 9b8e7705..4e003b9f 100644 --- a/codes/cgns/src/CgnsSection.cpp +++ b/codes/cgns/src/CgnsSection.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/src/CgnsVariable.cpp b/codes/cgns/src/CgnsVariable.cpp index c5711946..deb77820 100644 --- a/codes/cgns/src/CgnsVariable.cpp +++ b/codes/cgns/src/CgnsVariable.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/src/CgnsZbase.cpp b/codes/cgns/src/CgnsZbase.cpp index e8c7fbde..8bd8036d 100644 --- a/codes/cgns/src/CgnsZbase.cpp +++ b/codes/cgns/src/CgnsZbase.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/src/CgnsZbaseUtil.cpp b/codes/cgns/src/CgnsZbaseUtil.cpp index fe771ce2..afad4eab 100644 --- a/codes/cgns/src/CgnsZbaseUtil.cpp +++ b/codes/cgns/src/CgnsZbaseUtil.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/src/CgnsZbc.cpp b/codes/cgns/src/CgnsZbc.cpp index 1f57aae7..4162ac08 100644 --- a/codes/cgns/src/CgnsZbc.cpp +++ b/codes/cgns/src/CgnsZbc.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/src/CgnsZbc1to1.cpp b/codes/cgns/src/CgnsZbc1to1.cpp index 9d8ebbf8..0d58feb6 100644 --- a/codes/cgns/src/CgnsZbc1to1.cpp +++ b/codes/cgns/src/CgnsZbc1to1.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/src/CgnsZbcBoco.cpp b/codes/cgns/src/CgnsZbcBoco.cpp index 83f7b183..1c9b070b 100644 --- a/codes/cgns/src/CgnsZbcBoco.cpp +++ b/codes/cgns/src/CgnsZbcBoco.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/src/CgnsZbcConn.cpp b/codes/cgns/src/CgnsZbcConn.cpp index 061b08eb..34891b35 100644 --- a/codes/cgns/src/CgnsZbcConn.cpp +++ b/codes/cgns/src/CgnsZbcConn.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/src/CgnsZone.cpp b/codes/cgns/src/CgnsZone.cpp index e1c5ed7a..3f752f4f 100644 --- a/codes/cgns/src/CgnsZone.cpp +++ b/codes/cgns/src/CgnsZone.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/src/CgnsZoneUtil.cpp b/codes/cgns/src/CgnsZoneUtil.cpp index a85b75cb..7d3c04c6 100644 --- a/codes/cgns/src/CgnsZoneUtil.cpp +++ b/codes/cgns/src/CgnsZoneUtil.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/src/CgnsZsection.cpp b/codes/cgns/src/CgnsZsection.cpp index f60e2281..e1398639 100644 --- a/codes/cgns/src/CgnsZsection.cpp +++ b/codes/cgns/src/CgnsZsection.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cgns/src/HXCgns.cpp b/codes/cgns/src/HXCgns.cpp index d06aa76f..3074878a 100644 --- a/codes/cgns/src/HXCgns.cpp +++ b/codes/cgns/src/HXCgns.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/chemical/include/BlotterCurve.h b/codes/chemical/include/BlotterCurve.h index d1f9dbe6..73564b52 100644 --- a/codes/chemical/include/BlotterCurve.h +++ b/codes/chemical/include/BlotterCurve.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/chemical/include/Chemical.h b/codes/chemical/include/Chemical.h index 8a736847..7d9114f0 100644 --- a/codes/chemical/include/Chemical.h +++ b/codes/chemical/include/Chemical.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/chemical/include/MolecularProperty.h b/codes/chemical/include/MolecularProperty.h index 042163a6..9fdaaaa2 100644 --- a/codes/chemical/include/MolecularProperty.h +++ b/codes/chemical/include/MolecularProperty.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/chemical/include/ReactionRate.h b/codes/chemical/include/ReactionRate.h index c557c2d0..c00ffb0f 100644 --- a/codes/chemical/include/ReactionRate.h +++ b/codes/chemical/include/ReactionRate.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/chemical/include/SchmidtNumber.h b/codes/chemical/include/SchmidtNumber.h index 3e7c4dbe..d173550a 100644 --- a/codes/chemical/include/SchmidtNumber.h +++ b/codes/chemical/include/SchmidtNumber.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/chemical/include/Stoichiometric.h b/codes/chemical/include/Stoichiometric.h index 53d39052..c3402cd8 100644 --- a/codes/chemical/include/Stoichiometric.h +++ b/codes/chemical/include/Stoichiometric.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/chemical/include/Thermodynamic.h b/codes/chemical/include/Thermodynamic.h index 3716f3c4..c964c0de 100644 --- a/codes/chemical/include/Thermodynamic.h +++ b/codes/chemical/include/Thermodynamic.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/chemical/src/BlotterCurve.cpp b/codes/chemical/src/BlotterCurve.cpp index ee74f99a..81feeb19 100644 --- a/codes/chemical/src/BlotterCurve.cpp +++ b/codes/chemical/src/BlotterCurve.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/chemical/src/Chemical.cpp b/codes/chemical/src/Chemical.cpp index 9b20eace..ae1713e2 100644 --- a/codes/chemical/src/Chemical.cpp +++ b/codes/chemical/src/Chemical.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/chemical/src/MolecularProperty.cpp b/codes/chemical/src/MolecularProperty.cpp index c58bf4ef..51e1f3b2 100644 --- a/codes/chemical/src/MolecularProperty.cpp +++ b/codes/chemical/src/MolecularProperty.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/chemical/src/ReactionRate.cpp b/codes/chemical/src/ReactionRate.cpp index 65ea14ac..25d7c37a 100644 --- a/codes/chemical/src/ReactionRate.cpp +++ b/codes/chemical/src/ReactionRate.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/chemical/src/SchmidtNumber.cpp b/codes/chemical/src/SchmidtNumber.cpp index 6b5a35cb..a1b1f5f0 100644 --- a/codes/chemical/src/SchmidtNumber.cpp +++ b/codes/chemical/src/SchmidtNumber.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/chemical/src/Stoichiometric.cpp b/codes/chemical/src/Stoichiometric.cpp index 72a43c6a..571cc6d9 100644 --- a/codes/chemical/src/Stoichiometric.cpp +++ b/codes/chemical/src/Stoichiometric.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/chemical/src/Thermodynamic.cpp b/codes/chemical/src/Thermodynamic.cpp index 581996e1..a3fcc370 100644 --- a/codes/chemical/src/Thermodynamic.cpp +++ b/codes/chemical/src/Thermodynamic.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cuda/include/HybridParallel.h b/codes/cuda/include/HybridParallel.h index 828073b1..42b4228d 100644 --- a/codes/cuda/include/HybridParallel.h +++ b/codes/cuda/include/HybridParallel.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/cuda/src/HybridParallel.cpp b/codes/cuda/src/HybridParallel.cpp index 6931dfcf..2211eacc 100644 --- a/codes/cuda/src/HybridParallel.cpp +++ b/codes/cuda/src/HybridParallel.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/database/include/DataBase.h b/codes/database/include/DataBase.h index f1522b89..5e1aa3c5 100644 --- a/codes/database/include/DataBase.h +++ b/codes/database/include/DataBase.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/database/include/DataBaseIO.h b/codes/database/include/DataBaseIO.h index e622952a..9a577afb 100644 --- a/codes/database/include/DataBaseIO.h +++ b/codes/database/include/DataBaseIO.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/database/include/DataBaseType.h b/codes/database/include/DataBaseType.h index 81251482..54a9df2b 100644 --- a/codes/database/include/DataBaseType.h +++ b/codes/database/include/DataBaseType.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/database/include/DataBook.h b/codes/database/include/DataBook.h index 87d0bdce..8186efd6 100644 --- a/codes/database/include/DataBook.h +++ b/codes/database/include/DataBook.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/database/include/DataField.h b/codes/database/include/DataField.h index 51d3826d..919e21aa 100644 --- a/codes/database/include/DataField.h +++ b/codes/database/include/DataField.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/database/include/DataObject.h b/codes/database/include/DataObject.h index 58714394..4340b5dd 100644 --- a/codes/database/include/DataObject.h +++ b/codes/database/include/DataObject.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/database/include/DataPage.h b/codes/database/include/DataPage.h index 2d93fdd5..62fd2b2f 100644 --- a/codes/database/include/DataPage.h +++ b/codes/database/include/DataPage.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/database/include/DataPara.h b/codes/database/include/DataPara.h index e62b37b0..722e5df1 100644 --- a/codes/database/include/DataPara.h +++ b/codes/database/include/DataPara.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/database/include/DataPointer.h b/codes/database/include/DataPointer.h index 41a1a7d9..b9abb9ed 100644 --- a/codes/database/include/DataPointer.h +++ b/codes/database/include/DataPointer.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/database/include/DataStorage.h b/codes/database/include/DataStorage.h index 77e381f3..2713c0b0 100644 --- a/codes/database/include/DataStorage.h +++ b/codes/database/include/DataStorage.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/database/src/DataBase.cpp b/codes/database/src/DataBase.cpp index 5274b2b3..a7eed3cc 100644 --- a/codes/database/src/DataBase.cpp +++ b/codes/database/src/DataBase.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/database/src/DataBaseIO.cpp b/codes/database/src/DataBaseIO.cpp index a78051c8..bac00daf 100644 --- a/codes/database/src/DataBaseIO.cpp +++ b/codes/database/src/DataBaseIO.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/database/src/DataBaseType.cpp b/codes/database/src/DataBaseType.cpp index f13397d4..5d8a0ebf 100644 --- a/codes/database/src/DataBaseType.cpp +++ b/codes/database/src/DataBaseType.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/database/src/DataBook.cpp b/codes/database/src/DataBook.cpp index 16ed3e99..b00f1a86 100644 --- a/codes/database/src/DataBook.cpp +++ b/codes/database/src/DataBook.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/database/src/DataField.cpp b/codes/database/src/DataField.cpp index f3d687ab..013621af 100644 --- a/codes/database/src/DataField.cpp +++ b/codes/database/src/DataField.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/database/src/DataPage.cpp b/codes/database/src/DataPage.cpp index bc0c3e59..2e740363 100644 --- a/codes/database/src/DataPage.cpp +++ b/codes/database/src/DataPage.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/database/src/DataPara.cpp b/codes/database/src/DataPara.cpp index 09b8e30b..908bdb82 100644 --- a/codes/database/src/DataPara.cpp +++ b/codes/database/src/DataPara.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/database/src/DataStorage.cpp b/codes/database/src/DataStorage.cpp index 18e65b8a..e6fd0646 100644 --- a/codes/database/src/DataStorage.cpp +++ b/codes/database/src/DataStorage.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/force/include/AeroForce.h b/codes/force/include/AeroForce.h index 84d8584d..0ae2004d 100644 --- a/codes/force/include/AeroForce.h +++ b/codes/force/include/AeroForce.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/force/include/AeroForceTask.h b/codes/force/include/AeroForceTask.h index 7544165b..d62a60f2 100644 --- a/codes/force/include/AeroForceTask.h +++ b/codes/force/include/AeroForceTask.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/force/include/AeroForceTaskReg.h b/codes/force/include/AeroForceTaskReg.h index 396082f8..9fa9652f 100644 --- a/codes/force/include/AeroForceTaskReg.h +++ b/codes/force/include/AeroForceTaskReg.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/force/include/Force.h b/codes/force/include/Force.h index a5a9ae4d..875da7da 100644 --- a/codes/force/include/Force.h +++ b/codes/force/include/Force.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/force/src/AeroForce.cpp b/codes/force/src/AeroForce.cpp index a0f3e06d..b3636a1a 100644 --- a/codes/force/src/AeroForce.cpp +++ b/codes/force/src/AeroForce.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/force/src/AeroForceTask.cpp b/codes/force/src/AeroForceTask.cpp index a9cd3d7d..eab8d82b 100644 --- a/codes/force/src/AeroForceTask.cpp +++ b/codes/force/src/AeroForceTask.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/force/src/AeroForceTaskReg.cpp b/codes/force/src/AeroForceTaskReg.cpp index 3dbaf6d0..c72b7b92 100644 --- a/codes/force/src/AeroForceTaskReg.cpp +++ b/codes/force/src/AeroForceTaskReg.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/force/src/Force.cpp b/codes/force/src/Force.cpp index 3c2f2bd9..66dc47b2 100644 --- a/codes/force/src/Force.cpp +++ b/codes/force/src/Force.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/BcRecord.h b/codes/geometry/include/BcRecord.h index 47b05771..cbd97a5e 100644 --- a/codes/geometry/include/BcRecord.h +++ b/codes/geometry/include/BcRecord.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/Boundary.h b/codes/geometry/include/Boundary.h index 259e437f..01abc8bd 100644 --- a/codes/geometry/include/Boundary.h +++ b/codes/geometry/include/Boundary.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/CalcGrid.h b/codes/geometry/include/CalcGrid.h index 17195f0a..2397c45b 100644 --- a/codes/geometry/include/CalcGrid.h +++ b/codes/geometry/include/CalcGrid.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/CellMesh.h b/codes/geometry/include/CellMesh.h index 405b6fa9..13b66758 100644 --- a/codes/geometry/include/CellMesh.h +++ b/codes/geometry/include/CellMesh.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/CellTopo.h b/codes/geometry/include/CellTopo.h index a9fb501d..c4757510 100644 --- a/codes/geometry/include/CellTopo.h +++ b/codes/geometry/include/CellTopo.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/DomainInp.h b/codes/geometry/include/DomainInp.h index a4fceb01..55ac8427 100644 --- a/codes/geometry/include/DomainInp.h +++ b/codes/geometry/include/DomainInp.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/ElemFeature.h b/codes/geometry/include/ElemFeature.h index 894594d9..b537f1fa 100644 --- a/codes/geometry/include/ElemFeature.h +++ b/codes/geometry/include/ElemFeature.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/ElementHome.h b/codes/geometry/include/ElementHome.h index 006f0d75..80ec0968 100644 --- a/codes/geometry/include/ElementHome.h +++ b/codes/geometry/include/ElementHome.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/FaceMesh.h b/codes/geometry/include/FaceMesh.h index d486a73b..8102a445 100644 --- a/codes/geometry/include/FaceMesh.h +++ b/codes/geometry/include/FaceMesh.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/FaceSearch.h b/codes/geometry/include/FaceSearch.h index b8c0b147..177b852b 100644 --- a/codes/geometry/include/FaceSearch.h +++ b/codes/geometry/include/FaceSearch.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/FaceSolver.h b/codes/geometry/include/FaceSolver.h index 1d5aa399..dc12f596 100644 --- a/codes/geometry/include/FaceSolver.h +++ b/codes/geometry/include/FaceSolver.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/FaceTopo.h b/codes/geometry/include/FaceTopo.h index 0d46200c..df32f9e3 100644 --- a/codes/geometry/include/FaceTopo.h +++ b/codes/geometry/include/FaceTopo.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/Grid.h b/codes/geometry/include/Grid.h index a5da1e01..4e757fdb 100644 --- a/codes/geometry/include/Grid.h +++ b/codes/geometry/include/Grid.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/GridDef.h b/codes/geometry/include/GridDef.h index 9547ef44..af8f9b04 100644 --- a/codes/geometry/include/GridDef.h +++ b/codes/geometry/include/GridDef.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/GridElem.h b/codes/geometry/include/GridElem.h index 7594e8dc..58190cea 100644 --- a/codes/geometry/include/GridElem.h +++ b/codes/geometry/include/GridElem.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/GridFactory.h b/codes/geometry/include/GridFactory.h index 74f1e04e..b7b35405 100644 --- a/codes/geometry/include/GridFactory.h +++ b/codes/geometry/include/GridFactory.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/GridMediator.h b/codes/geometry/include/GridMediator.h index ff91d57f..66a76050 100644 --- a/codes/geometry/include/GridMediator.h +++ b/codes/geometry/include/GridMediator.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/GridPara.h b/codes/geometry/include/GridPara.h index c6cb745d..b66b2784 100644 --- a/codes/geometry/include/GridPara.h +++ b/codes/geometry/include/GridPara.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/GridTask.h b/codes/geometry/include/GridTask.h index 7a3f61da..adb4145b 100644 --- a/codes/geometry/include/GridTask.h +++ b/codes/geometry/include/GridTask.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/IFaceLink.h b/codes/geometry/include/IFaceLink.h index aeeb1d74..723ab211 100644 --- a/codes/geometry/include/IFaceLink.h +++ b/codes/geometry/include/IFaceLink.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/InterFace.h b/codes/geometry/include/InterFace.h index e35a6db1..888a334b 100644 --- a/codes/geometry/include/InterFace.h +++ b/codes/geometry/include/InterFace.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/NodeMesh.h b/codes/geometry/include/NodeMesh.h index d32a810b..e4d10dd2 100644 --- a/codes/geometry/include/NodeMesh.h +++ b/codes/geometry/include/NodeMesh.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/Plot3D.h b/codes/geometry/include/Plot3D.h index cf4b5599..21233c9a 100644 --- a/codes/geometry/include/Plot3D.h +++ b/codes/geometry/include/Plot3D.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/Point.h b/codes/geometry/include/Point.h index 42824b2f..9fb6ad65 100644 --- a/codes/geometry/include/Point.h +++ b/codes/geometry/include/Point.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/PointFactory.h b/codes/geometry/include/PointFactory.h index f4be43db..335da9e7 100644 --- a/codes/geometry/include/PointFactory.h +++ b/codes/geometry/include/PointFactory.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/PointSearch.h b/codes/geometry/include/PointSearch.h index 40b7eb5d..59fc2385 100644 --- a/codes/geometry/include/PointSearch.h +++ b/codes/geometry/include/PointSearch.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/SlipFace.h b/codes/geometry/include/SlipFace.h index 73aca811..06c16d02 100644 --- a/codes/geometry/include/SlipFace.h +++ b/codes/geometry/include/SlipFace.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/StrGrid.h b/codes/geometry/include/StrGrid.h index 03e2e53c..b47d6987 100644 --- a/codes/geometry/include/StrGrid.h +++ b/codes/geometry/include/StrGrid.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/StrRegion.h b/codes/geometry/include/StrRegion.h index 947a693c..73770c20 100644 --- a/codes/geometry/include/StrRegion.h +++ b/codes/geometry/include/StrRegion.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/Su2Grid.h b/codes/geometry/include/Su2Grid.h index 2e15c81b..1840d8c4 100644 --- a/codes/geometry/include/Su2Grid.h +++ b/codes/geometry/include/Su2Grid.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/UnitElement.h b/codes/geometry/include/UnitElement.h index d06f4d0a..ce4489bb 100644 --- a/codes/geometry/include/UnitElement.h +++ b/codes/geometry/include/UnitElement.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/UnsGrid.h b/codes/geometry/include/UnsGrid.h index a2105419..64829970 100644 --- a/codes/geometry/include/UnsGrid.h +++ b/codes/geometry/include/UnsGrid.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/Visual.h b/codes/geometry/include/Visual.h index 0b68841c..059c79e9 100644 --- a/codes/geometry/include/Visual.h +++ b/codes/geometry/include/Visual.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/include/WallDist.h b/codes/geometry/include/WallDist.h index 3a19c9c8..bed15ed0 100644 --- a/codes/geometry/include/WallDist.h +++ b/codes/geometry/include/WallDist.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/BcRecord.cpp b/codes/geometry/src/BcRecord.cpp index 27baaa57..09cf1e38 100644 --- a/codes/geometry/src/BcRecord.cpp +++ b/codes/geometry/src/BcRecord.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/Boundary.cpp b/codes/geometry/src/Boundary.cpp index a293eded..968196c5 100644 --- a/codes/geometry/src/Boundary.cpp +++ b/codes/geometry/src/Boundary.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/CalcGrid.cpp b/codes/geometry/src/CalcGrid.cpp index 4cd97144..3fb73aa5 100644 --- a/codes/geometry/src/CalcGrid.cpp +++ b/codes/geometry/src/CalcGrid.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/CellMesh.cpp b/codes/geometry/src/CellMesh.cpp index 3c327864..66804f6f 100644 --- a/codes/geometry/src/CellMesh.cpp +++ b/codes/geometry/src/CellMesh.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/CellTopo.cpp b/codes/geometry/src/CellTopo.cpp index deca7073..7bd85102 100644 --- a/codes/geometry/src/CellTopo.cpp +++ b/codes/geometry/src/CellTopo.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/DomainInp.cpp b/codes/geometry/src/DomainInp.cpp index aa027508..17e15428 100644 --- a/codes/geometry/src/DomainInp.cpp +++ b/codes/geometry/src/DomainInp.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/ElemFeature.cpp b/codes/geometry/src/ElemFeature.cpp index 16d559f1..ae238bfc 100644 --- a/codes/geometry/src/ElemFeature.cpp +++ b/codes/geometry/src/ElemFeature.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/ElementHome.cpp b/codes/geometry/src/ElementHome.cpp index 47786aa8..82874145 100644 --- a/codes/geometry/src/ElementHome.cpp +++ b/codes/geometry/src/ElementHome.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/FaceMesh.cpp b/codes/geometry/src/FaceMesh.cpp index a5649a3c..c9147fc8 100644 --- a/codes/geometry/src/FaceMesh.cpp +++ b/codes/geometry/src/FaceMesh.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/FaceSearch.cpp b/codes/geometry/src/FaceSearch.cpp index a7e038f0..84cd704a 100644 --- a/codes/geometry/src/FaceSearch.cpp +++ b/codes/geometry/src/FaceSearch.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/FaceSolver.cpp b/codes/geometry/src/FaceSolver.cpp index 6a585ab7..be4948c2 100644 --- a/codes/geometry/src/FaceSolver.cpp +++ b/codes/geometry/src/FaceSolver.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/FaceTopo.cpp b/codes/geometry/src/FaceTopo.cpp index d6c5c548..3dcb3377 100644 --- a/codes/geometry/src/FaceTopo.cpp +++ b/codes/geometry/src/FaceTopo.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/Grid.cpp b/codes/geometry/src/Grid.cpp index 9a106204..27330700 100644 --- a/codes/geometry/src/Grid.cpp +++ b/codes/geometry/src/Grid.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/GridDef.cpp b/codes/geometry/src/GridDef.cpp index 95486404..442cdd80 100644 --- a/codes/geometry/src/GridDef.cpp +++ b/codes/geometry/src/GridDef.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/GridElem.cpp b/codes/geometry/src/GridElem.cpp index 3a1e6a02..05b74384 100644 --- a/codes/geometry/src/GridElem.cpp +++ b/codes/geometry/src/GridElem.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/GridFactory.cpp b/codes/geometry/src/GridFactory.cpp index 95245147..04e2562e 100644 --- a/codes/geometry/src/GridFactory.cpp +++ b/codes/geometry/src/GridFactory.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/GridMediator.cpp b/codes/geometry/src/GridMediator.cpp index 2da43e5c..3930742e 100644 --- a/codes/geometry/src/GridMediator.cpp +++ b/codes/geometry/src/GridMediator.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/GridPara.cpp b/codes/geometry/src/GridPara.cpp index 28452172..2d2b127b 100644 --- a/codes/geometry/src/GridPara.cpp +++ b/codes/geometry/src/GridPara.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/GridTask.cpp b/codes/geometry/src/GridTask.cpp index 706cdaab..84957d2a 100644 --- a/codes/geometry/src/GridTask.cpp +++ b/codes/geometry/src/GridTask.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/IFaceLink.cpp b/codes/geometry/src/IFaceLink.cpp index c3ecc6d2..8605629e 100644 --- a/codes/geometry/src/IFaceLink.cpp +++ b/codes/geometry/src/IFaceLink.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/InterFace.cpp b/codes/geometry/src/InterFace.cpp index a19bc3a4..0541e608 100644 --- a/codes/geometry/src/InterFace.cpp +++ b/codes/geometry/src/InterFace.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/NodeMesh.cpp b/codes/geometry/src/NodeMesh.cpp index 1a09f710..17076106 100644 --- a/codes/geometry/src/NodeMesh.cpp +++ b/codes/geometry/src/NodeMesh.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/Plot3D.cpp b/codes/geometry/src/Plot3D.cpp index e6a56937..8034036c 100644 --- a/codes/geometry/src/Plot3D.cpp +++ b/codes/geometry/src/Plot3D.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/PointFactory.cpp b/codes/geometry/src/PointFactory.cpp index 6c87e3d6..8c60f7e5 100644 --- a/codes/geometry/src/PointFactory.cpp +++ b/codes/geometry/src/PointFactory.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/PointSearch.cpp b/codes/geometry/src/PointSearch.cpp index bc457810..53a8adfa 100644 --- a/codes/geometry/src/PointSearch.cpp +++ b/codes/geometry/src/PointSearch.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/SlipFace.cpp b/codes/geometry/src/SlipFace.cpp index 601e0a74..ccd48211 100644 --- a/codes/geometry/src/SlipFace.cpp +++ b/codes/geometry/src/SlipFace.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/StrGrid.cpp b/codes/geometry/src/StrGrid.cpp index 971bf185..4b2998c9 100644 --- a/codes/geometry/src/StrGrid.cpp +++ b/codes/geometry/src/StrGrid.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/StrRegion.cpp b/codes/geometry/src/StrRegion.cpp index 6e91b183..4b6a9145 100644 --- a/codes/geometry/src/StrRegion.cpp +++ b/codes/geometry/src/StrRegion.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/Su2Grid.cpp b/codes/geometry/src/Su2Grid.cpp index d679e737..f88fc6bf 100644 --- a/codes/geometry/src/Su2Grid.cpp +++ b/codes/geometry/src/Su2Grid.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/UnitElement.cpp b/codes/geometry/src/UnitElement.cpp index 51070cc4..4df5c21d 100644 --- a/codes/geometry/src/UnitElement.cpp +++ b/codes/geometry/src/UnitElement.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/UnsGrid.cpp b/codes/geometry/src/UnsGrid.cpp index f91e69d5..acc65962 100644 --- a/codes/geometry/src/UnsGrid.cpp +++ b/codes/geometry/src/UnsGrid.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/Visual.cpp b/codes/geometry/src/Visual.cpp index 0fcecb2f..d8f20c54 100644 --- a/codes/geometry/src/Visual.cpp +++ b/codes/geometry/src/Visual.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/geometry/src/WallDist.cpp b/codes/geometry/src/WallDist.cpp index 8ad3df7f..16aaf02b 100644 --- a/codes/geometry/src/WallDist.cpp +++ b/codes/geometry/src/WallDist.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/include/Com.h b/codes/global/include/Com.h index d1f169d6..a82e2937 100644 --- a/codes/global/include/Com.h +++ b/codes/global/include/Com.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/include/Ctrl.h b/codes/global/include/Ctrl.h index 910839b4..affc2d57 100644 --- a/codes/global/include/Ctrl.h +++ b/codes/global/include/Ctrl.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/include/Dimension.h b/codes/global/include/Dimension.h index 389c9a7a..20573524 100644 --- a/codes/global/include/Dimension.h +++ b/codes/global/include/Dimension.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/include/DimensionImp.h b/codes/global/include/DimensionImp.h index d3381b8c..7f8f27db 100644 --- a/codes/global/include/DimensionImp.h +++ b/codes/global/include/DimensionImp.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/include/FieldAlloc.h b/codes/global/include/FieldAlloc.h index af490e08..20a4389a 100644 --- a/codes/global/include/FieldAlloc.h +++ b/codes/global/include/FieldAlloc.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/include/FieldBase.h b/codes/global/include/FieldBase.h index 1b55fa0b..20cd314c 100644 --- a/codes/global/include/FieldBase.h +++ b/codes/global/include/FieldBase.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/include/FieldImp.h b/codes/global/include/FieldImp.h index 46483763..156259a6 100644 --- a/codes/global/include/FieldImp.h +++ b/codes/global/include/FieldImp.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/include/FieldRecord.h b/codes/global/include/FieldRecord.h index eb4370c1..4ae14b92 100644 --- a/codes/global/include/FieldRecord.h +++ b/codes/global/include/FieldRecord.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/include/FieldSimu.h b/codes/global/include/FieldSimu.h index dccb80df..dd1991d9 100644 --- a/codes/global/include/FieldSimu.h +++ b/codes/global/include/FieldSimu.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/include/FileMap.h b/codes/global/include/FileMap.h index 3ac27dfc..bb64c397 100644 --- a/codes/global/include/FileMap.h +++ b/codes/global/include/FileMap.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/include/Iteration.h b/codes/global/include/Iteration.h index 213119b2..a54f28ea 100644 --- a/codes/global/include/Iteration.h +++ b/codes/global/include/Iteration.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/include/SimuDef.h b/codes/global/include/SimuDef.h index 2fa3cf24..ae049a22 100644 --- a/codes/global/include/SimuDef.h +++ b/codes/global/include/SimuDef.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/include/SolverDef.h b/codes/global/include/SolverDef.h index 19c6d2d3..8389c0dc 100644 --- a/codes/global/include/SolverDef.h +++ b/codes/global/include/SolverDef.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/include/SolverName.h b/codes/global/include/SolverName.h index 95df20d6..6fc9cb6b 100644 --- a/codes/global/include/SolverName.h +++ b/codes/global/include/SolverName.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/include/SolverRegister.h b/codes/global/include/SolverRegister.h index 812cd8a3..5b78b866 100644 --- a/codes/global/include/SolverRegister.h +++ b/codes/global/include/SolverRegister.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/include/SolverTaskReg.h b/codes/global/include/SolverTaskReg.h index 22febf6a..7a673463 100644 --- a/codes/global/include/SolverTaskReg.h +++ b/codes/global/include/SolverTaskReg.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/include/System.h b/codes/global/include/System.h index 45407aab..18fbf486 100644 --- a/codes/global/include/System.h +++ b/codes/global/include/System.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/include/Tolerence.h b/codes/global/include/Tolerence.h index 42a01000..49ee28bc 100644 --- a/codes/global/include/Tolerence.h +++ b/codes/global/include/Tolerence.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/src/Com.cpp b/codes/global/src/Com.cpp index af20592d..98d7c1f0 100644 --- a/codes/global/src/Com.cpp +++ b/codes/global/src/Com.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/src/Ctrl.cpp b/codes/global/src/Ctrl.cpp index 803132a0..dac15158 100644 --- a/codes/global/src/Ctrl.cpp +++ b/codes/global/src/Ctrl.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/src/Dimension.cpp b/codes/global/src/Dimension.cpp index 29cc0d83..ce1c314c 100644 --- a/codes/global/src/Dimension.cpp +++ b/codes/global/src/Dimension.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/src/DimensionImp.cpp b/codes/global/src/DimensionImp.cpp index 599d051d..56497996 100644 --- a/codes/global/src/DimensionImp.cpp +++ b/codes/global/src/DimensionImp.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/src/FieldAlloc.cpp b/codes/global/src/FieldAlloc.cpp index 1f706e4a..1ce75d5b 100644 --- a/codes/global/src/FieldAlloc.cpp +++ b/codes/global/src/FieldAlloc.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/src/FieldBase.cpp b/codes/global/src/FieldBase.cpp index 7bf47e49..3bbd997f 100644 --- a/codes/global/src/FieldBase.cpp +++ b/codes/global/src/FieldBase.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/src/FieldImp.cpp b/codes/global/src/FieldImp.cpp index a2b9badf..2053b5f6 100644 --- a/codes/global/src/FieldImp.cpp +++ b/codes/global/src/FieldImp.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/src/FieldRecord.cpp b/codes/global/src/FieldRecord.cpp index 10b869e5..25ac589e 100644 --- a/codes/global/src/FieldRecord.cpp +++ b/codes/global/src/FieldRecord.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/src/FieldSimu.cpp b/codes/global/src/FieldSimu.cpp index f905467b..f066c967 100644 --- a/codes/global/src/FieldSimu.cpp +++ b/codes/global/src/FieldSimu.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/src/FileMap.cpp b/codes/global/src/FileMap.cpp index 2a0d30f5..088e4ded 100644 --- a/codes/global/src/FileMap.cpp +++ b/codes/global/src/FileMap.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/src/Interation.cpp b/codes/global/src/Interation.cpp index 64dd8e0b..c3ab9668 100644 --- a/codes/global/src/Interation.cpp +++ b/codes/global/src/Interation.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/src/SimuDef.cpp b/codes/global/src/SimuDef.cpp index feea3030..da023efa 100644 --- a/codes/global/src/SimuDef.cpp +++ b/codes/global/src/SimuDef.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/src/SolverDef.cpp b/codes/global/src/SolverDef.cpp index 669dd10e..93a0bf54 100644 --- a/codes/global/src/SolverDef.cpp +++ b/codes/global/src/SolverDef.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/src/SolverName.cpp b/codes/global/src/SolverName.cpp index b75bbd75..6a3da524 100644 --- a/codes/global/src/SolverName.cpp +++ b/codes/global/src/SolverName.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/src/SolverRegister.cpp b/codes/global/src/SolverRegister.cpp index 01497f7e..2b6eda09 100644 --- a/codes/global/src/SolverRegister.cpp +++ b/codes/global/src/SolverRegister.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/src/SolverTaskReg.cpp b/codes/global/src/SolverTaskReg.cpp index ee3d5729..fb600bfb 100644 --- a/codes/global/src/SolverTaskReg.cpp +++ b/codes/global/src/SolverTaskReg.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/src/System.cpp b/codes/global/src/System.cpp index 9b32374b..3b267782 100644 --- a/codes/global/src/System.cpp +++ b/codes/global/src/System.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/global/src/Tolerence.cpp b/codes/global/src/Tolerence.cpp index 1dd8baa7..ed6849d5 100644 --- a/codes/global/src/Tolerence.cpp +++ b/codes/global/src/Tolerence.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/grad/include/Grad.h b/codes/grad/include/Grad.h index 2d7fbe1d..9d7e9620 100644 --- a/codes/grad/include/Grad.h +++ b/codes/grad/include/Grad.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/grad/src/Grad.cpp b/codes/grad/src/Grad.cpp index aca3d4da..f27879e4 100644 --- a/codes/grad/src/Grad.cpp +++ b/codes/grad/src/Grad.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/implicit/include/ImplicitTaskReg.h b/codes/implicit/include/ImplicitTaskReg.h index 7acade1a..dd322ba5 100644 --- a/codes/implicit/include/ImplicitTaskReg.h +++ b/codes/implicit/include/ImplicitTaskReg.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/implicit/include/Lusgs.h b/codes/implicit/include/Lusgs.h index a914a014..38fb0203 100644 --- a/codes/implicit/include/Lusgs.h +++ b/codes/implicit/include/Lusgs.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/implicit/src/ImplicitTaskReg.cpp b/codes/implicit/src/ImplicitTaskReg.cpp index 1e6a73a1..8987608d 100644 --- a/codes/implicit/src/ImplicitTaskReg.cpp +++ b/codes/implicit/src/ImplicitTaskReg.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/implicit/src/Lusgs.cpp b/codes/implicit/src/Lusgs.cpp index f972f9b3..dd4f9cfb 100644 --- a/codes/implicit/src/Lusgs.cpp +++ b/codes/implicit/src/Lusgs.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/include/INsBcSolver.h b/codes/ins/include/INsBcSolver.h index 964bc38b..5a53b535 100644 --- a/codes/ins/include/INsBcSolver.h +++ b/codes/ins/include/INsBcSolver.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/include/INsCom.h b/codes/ins/include/INsCom.h index e6e0740c..1233b09a 100644 --- a/codes/ins/include/INsCom.h +++ b/codes/ins/include/INsCom.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/include/INsCtrl.h b/codes/ins/include/INsCtrl.h index 89bb9968..9cf2f9f9 100644 --- a/codes/ins/include/INsCtrl.h +++ b/codes/ins/include/INsCtrl.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/include/INsIdx.h b/codes/ins/include/INsIdx.h index 273b1fd8..365c34d3 100644 --- a/codes/ins/include/INsIdx.h +++ b/codes/ins/include/INsIdx.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/include/INsInvterm.h b/codes/ins/include/INsInvterm.h index fedd2828..0e817479 100644 --- a/codes/ins/include/INsInvterm.h +++ b/codes/ins/include/INsInvterm.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/include/INsLusgs.h b/codes/ins/include/INsLusgs.h index cf9dabe1..fc3d505f 100644 --- a/codes/ins/include/INsLusgs.h +++ b/codes/ins/include/INsLusgs.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/include/INsRestart.h b/codes/ins/include/INsRestart.h index 042d994d..e42454ed 100644 --- a/codes/ins/include/INsRestart.h +++ b/codes/ins/include/INsRestart.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/include/INsRhs.h b/codes/ins/include/INsRhs.h index a02e0599..918f80cc 100644 --- a/codes/ins/include/INsRhs.h +++ b/codes/ins/include/INsRhs.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/include/INsSolver.h b/codes/ins/include/INsSolver.h index 60994022..57a22855 100644 --- a/codes/ins/include/INsSolver.h +++ b/codes/ins/include/INsSolver.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/include/INsSolverImp.h b/codes/ins/include/INsSolverImp.h index 20da1d8a..ed7df88e 100644 --- a/codes/ins/include/INsSolverImp.h +++ b/codes/ins/include/INsSolverImp.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/include/INsSpectrum.h b/codes/ins/include/INsSpectrum.h index 27865753..f985a9ed 100644 --- a/codes/ins/include/INsSpectrum.h +++ b/codes/ins/include/INsSpectrum.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/include/INsUnsteady.h b/codes/ins/include/INsUnsteady.h index 179001c2..37e9476c 100644 --- a/codes/ins/include/INsUnsteady.h +++ b/codes/ins/include/INsUnsteady.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/include/INsUpdate.h b/codes/ins/include/INsUpdate.h index 83097ca8..411d90fe 100644 --- a/codes/ins/include/INsUpdate.h +++ b/codes/ins/include/INsUpdate.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/include/INsVisterm.h b/codes/ins/include/INsVisterm.h index 05d5eabf..19208bef 100644 --- a/codes/ins/include/INsVisterm.h +++ b/codes/ins/include/INsVisterm.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/src/INsBcSolver.cpp b/codes/ins/src/INsBcSolver.cpp index 6743a532..1fefa27e 100644 --- a/codes/ins/src/INsBcSolver.cpp +++ b/codes/ins/src/INsBcSolver.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/src/INsCom.cpp b/codes/ins/src/INsCom.cpp index 3cde5afc..08603838 100644 --- a/codes/ins/src/INsCom.cpp +++ b/codes/ins/src/INsCom.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/src/INsCtrl.cpp b/codes/ins/src/INsCtrl.cpp index 374127b6..51899dbf 100644 --- a/codes/ins/src/INsCtrl.cpp +++ b/codes/ins/src/INsCtrl.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/src/INsInvterm.cpp b/codes/ins/src/INsInvterm.cpp index 1bea1af7..147c2642 100644 --- a/codes/ins/src/INsInvterm.cpp +++ b/codes/ins/src/INsInvterm.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/src/INsLusgs.cpp b/codes/ins/src/INsLusgs.cpp index 92fec9f2..9a076c36 100644 --- a/codes/ins/src/INsLusgs.cpp +++ b/codes/ins/src/INsLusgs.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/src/INsRestart.cpp b/codes/ins/src/INsRestart.cpp index df7cfa4a..c9ccb7da 100644 --- a/codes/ins/src/INsRestart.cpp +++ b/codes/ins/src/INsRestart.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/src/INsRhs.cpp b/codes/ins/src/INsRhs.cpp index a211ae49..6d197baf 100644 --- a/codes/ins/src/INsRhs.cpp +++ b/codes/ins/src/INsRhs.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/src/INsSolver.cpp b/codes/ins/src/INsSolver.cpp index 197c15f4..f64be696 100644 --- a/codes/ins/src/INsSolver.cpp +++ b/codes/ins/src/INsSolver.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/src/INsSolverImp.cpp b/codes/ins/src/INsSolverImp.cpp index 35cc079f..ec26fb3a 100644 --- a/codes/ins/src/INsSolverImp.cpp +++ b/codes/ins/src/INsSolverImp.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/src/INsSpectrum.cpp b/codes/ins/src/INsSpectrum.cpp index e3c53e7e..4f6411bd 100644 --- a/codes/ins/src/INsSpectrum.cpp +++ b/codes/ins/src/INsSpectrum.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/src/INsUnsteady.cpp b/codes/ins/src/INsUnsteady.cpp index b08745e8..170c6ae5 100644 --- a/codes/ins/src/INsUnsteady.cpp +++ b/codes/ins/src/INsUnsteady.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/src/INsUpdate.cpp b/codes/ins/src/INsUpdate.cpp index f6465061..8ed2da2a 100644 --- a/codes/ins/src/INsUpdate.cpp +++ b/codes/ins/src/INsUpdate.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ins/src/INsVisterm.cpp b/codes/ins/src/INsVisterm.cpp index 4f1ea8e6..6c39498d 100644 --- a/codes/ins/src/INsVisterm.cpp +++ b/codes/ins/src/INsVisterm.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/interface/include/InterField.h b/codes/interface/include/InterField.h index 6933a6f4..913abbc5 100644 --- a/codes/interface/include/InterField.h +++ b/codes/interface/include/InterField.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/interface/include/InterfaceTaskReg.h b/codes/interface/include/InterfaceTaskReg.h index 7d9c9b17..af79550b 100644 --- a/codes/interface/include/InterfaceTaskReg.h +++ b/codes/interface/include/InterfaceTaskReg.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/interface/src/InterField.cpp b/codes/interface/src/InterField.cpp index df2b2554..8face9d4 100644 --- a/codes/interface/src/InterField.cpp +++ b/codes/interface/src/InterField.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/interface/src/InterfaceTaskReg.cpp b/codes/interface/src/InterfaceTaskReg.cpp index 182083a7..7e65fc22 100644 --- a/codes/interface/src/InterfaceTaskReg.cpp +++ b/codes/interface/src/InterfaceTaskReg.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/io/include/CommentLine.h b/codes/io/include/CommentLine.h index 272ef3c4..78dd53fa 100644 --- a/codes/io/include/CommentLine.h +++ b/codes/io/include/CommentLine.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/io/include/FileIO.h b/codes/io/include/FileIO.h index 649a40f0..bf0da52e 100644 --- a/codes/io/include/FileIO.h +++ b/codes/io/include/FileIO.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/io/include/FileO.h b/codes/io/include/FileO.h index 2dc3d586..8fd5f4ff 100644 --- a/codes/io/include/FileO.h +++ b/codes/io/include/FileO.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/io/include/LogFile.h b/codes/io/include/LogFile.h index 4c109d52..bd139298 100644 --- a/codes/io/include/LogFile.h +++ b/codes/io/include/LogFile.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/io/include/PIO.h b/codes/io/include/PIO.h index e89d58ef..6a917b0d 100644 --- a/codes/io/include/PIO.h +++ b/codes/io/include/PIO.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/io/include/ParaFile.h b/codes/io/include/ParaFile.h index 0660808b..727d6bcb 100644 --- a/codes/io/include/ParaFile.h +++ b/codes/io/include/ParaFile.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/io/include/StrUtil.h b/codes/io/include/StrUtil.h index a13e0bfa..97b6cee8 100644 --- a/codes/io/include/StrUtil.h +++ b/codes/io/include/StrUtil.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/io/include/Word.h b/codes/io/include/Word.h index 3b167bad..2cae7fc2 100644 --- a/codes/io/include/Word.h +++ b/codes/io/include/Word.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/io/src/CommentLine.cpp b/codes/io/src/CommentLine.cpp index ba61e704..884967a9 100644 --- a/codes/io/src/CommentLine.cpp +++ b/codes/io/src/CommentLine.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/io/src/FileIO.cpp b/codes/io/src/FileIO.cpp index 2de10417..14f9de20 100644 --- a/codes/io/src/FileIO.cpp +++ b/codes/io/src/FileIO.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/io/src/FileO.cpp b/codes/io/src/FileO.cpp index f61c5cd0..e2e76b4a 100644 --- a/codes/io/src/FileO.cpp +++ b/codes/io/src/FileO.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/io/src/LogFile.cpp b/codes/io/src/LogFile.cpp index 9b56744b..24c8bd1d 100644 --- a/codes/io/src/LogFile.cpp +++ b/codes/io/src/LogFile.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/io/src/PIO.cpp b/codes/io/src/PIO.cpp index 9a0ad785..09556822 100644 --- a/codes/io/src/PIO.cpp +++ b/codes/io/src/PIO.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/io/src/ParaFile.cpp b/codes/io/src/ParaFile.cpp index 698b8d50..8bda2ff2 100644 --- a/codes/io/src/ParaFile.cpp +++ b/codes/io/src/ParaFile.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/io/src/StrUtil.cpp b/codes/io/src/StrUtil.cpp index ee6e5953..5f82b18e 100644 --- a/codes/io/src/StrUtil.cpp +++ b/codes/io/src/StrUtil.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/io/src/Word.cpp b/codes/io/src/Word.cpp index 2414d3b7..a92db1d7 100644 --- a/codes/io/src/Word.cpp +++ b/codes/io/src/Word.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/lhs/include/Lhs.h b/codes/lhs/include/Lhs.h index 1d3c1235..c86cef36 100644 --- a/codes/lhs/include/Lhs.h +++ b/codes/lhs/include/Lhs.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/lhs/include/LhsTaskReg.h b/codes/lhs/include/LhsTaskReg.h index e8a2c62b..4bbca344 100644 --- a/codes/lhs/include/LhsTaskReg.h +++ b/codes/lhs/include/LhsTaskReg.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/lhs/src/Lhs.cpp b/codes/lhs/src/Lhs.cpp index 5a56f7c1..6d4b1116 100644 --- a/codes/lhs/src/Lhs.cpp +++ b/codes/lhs/src/Lhs.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/lhs/src/LhsTaskReg.cpp b/codes/lhs/src/LhsTaskReg.cpp index 0b3b6f20..cc5d854e 100644 --- a/codes/lhs/src/LhsTaskReg.cpp +++ b/codes/lhs/src/LhsTaskReg.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/main/include/SimpleSimu.h b/codes/main/include/SimpleSimu.h index da655524..1d4e0ce2 100644 --- a/codes/main/include/SimpleSimu.h +++ b/codes/main/include/SimpleSimu.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/main/include/SimuImp.h b/codes/main/include/SimuImp.h index 32d2024a..55211ca1 100644 --- a/codes/main/include/SimuImp.h +++ b/codes/main/include/SimuImp.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/main/include/Simulation.h b/codes/main/include/Simulation.h index f3fc2851..2e8c62f8 100644 --- a/codes/main/include/Simulation.h +++ b/codes/main/include/Simulation.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/main/src/OneFlow.cpp b/codes/main/src/OneFlow.cpp index dc7c0e25..e4906d8f 100644 --- a/codes/main/src/OneFlow.cpp +++ b/codes/main/src/OneFlow.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/main/src/SimpleSimu.cpp b/codes/main/src/SimpleSimu.cpp index e0682c55..ebb99af3 100644 --- a/codes/main/src/SimpleSimu.cpp +++ b/codes/main/src/SimpleSimu.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/main/src/SimuImp.cpp b/codes/main/src/SimuImp.cpp index fb8dd48a..3c9ed616 100644 --- a/codes/main/src/SimuImp.cpp +++ b/codes/main/src/SimuImp.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/main/src/Simulation.cpp b/codes/main/src/Simulation.cpp index 68b5e1a5..645f065f 100644 --- a/codes/main/src/Simulation.cpp +++ b/codes/main/src/Simulation.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/math/include/HXMath.h b/codes/math/include/HXMath.h index 1057a630..27b7b33c 100644 --- a/codes/math/include/HXMath.h +++ b/codes/math/include/HXMath.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/math/include/HXMathExt.h b/codes/math/include/HXMathExt.h index 33baf9c1..ca1e4ffa 100644 --- a/codes/math/include/HXMathExt.h +++ b/codes/math/include/HXMathExt.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/math/src/HXMath.cpp b/codes/math/src/HXMath.cpp index a3c6255e..2adad38e 100644 --- a/codes/math/src/HXMath.cpp +++ b/codes/math/src/HXMath.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/math/src/HXMathExt.cpp b/codes/math/src/HXMathExt.cpp index 6b30b9ef..e0c6c118 100644 --- a/codes/math/src/HXMathExt.cpp +++ b/codes/math/src/HXMathExt.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/model/include/FlowModel.h b/codes/model/include/FlowModel.h index ea29f68e..32467955 100644 --- a/codes/model/include/FlowModel.h +++ b/codes/model/include/FlowModel.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/model/include/Sutherland.h b/codes/model/include/Sutherland.h index 3761adbf..f73325ce 100644 --- a/codes/model/include/Sutherland.h +++ b/codes/model/include/Sutherland.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/model/src/FlowModel.cpp b/codes/model/src/FlowModel.cpp index dd4931fb..79bd6185 100644 --- a/codes/model/src/FlowModel.cpp +++ b/codes/model/src/FlowModel.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/model/src/Sutherland.cpp b/codes/model/src/Sutherland.cpp index 6446fc2b..1232af2c 100644 --- a/codes/model/src/Sutherland.cpp +++ b/codes/model/src/Sutherland.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/multiarray/include/Memop.h b/codes/multiarray/include/Memop.h index a8e9d99f..c4cdda01 100644 --- a/codes/multiarray/include/Memop.h +++ b/codes/multiarray/include/Memop.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/multiarray/include/Multiarray.h b/codes/multiarray/include/Multiarray.h index f88a1d9e..bf5b45d3 100644 --- a/codes/multiarray/include/Multiarray.h +++ b/codes/multiarray/include/Multiarray.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/multiarray/include/Range.h b/codes/multiarray/include/Range.h index 646a1436..130624b4 100644 --- a/codes/multiarray/include/Range.h +++ b/codes/multiarray/include/Range.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/multiarray/src/Memop.cpp b/codes/multiarray/src/Memop.cpp index 5734b7f7..deb1afd0 100644 --- a/codes/multiarray/src/Memop.cpp +++ b/codes/multiarray/src/Memop.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/multiarray/src/Multiarray.cpp b/codes/multiarray/src/Multiarray.cpp index 5734b7f7..deb1afd0 100644 --- a/codes/multiarray/src/Multiarray.cpp +++ b/codes/multiarray/src/Multiarray.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/multiarray/src/Range.cpp b/codes/multiarray/src/Range.cpp index 325d14d2..56df0c78 100644 --- a/codes/multiarray/src/Range.cpp +++ b/codes/multiarray/src/Range.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/multigrid/include/BgField.h b/codes/multigrid/include/BgField.h index 8e27b9de..bef25404 100644 --- a/codes/multigrid/include/BgField.h +++ b/codes/multigrid/include/BgField.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/multigrid/include/BgGrid.h b/codes/multigrid/include/BgGrid.h index 35a1dab4..3e8bc1bc 100644 --- a/codes/multigrid/include/BgGrid.h +++ b/codes/multigrid/include/BgGrid.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/multigrid/include/Multigrid.h b/codes/multigrid/include/Multigrid.h index 6608f860..71ef2579 100644 --- a/codes/multigrid/include/Multigrid.h +++ b/codes/multigrid/include/Multigrid.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/multigrid/include/MultigridTaskReg.h b/codes/multigrid/include/MultigridTaskReg.h index 7688dea7..1c7981df 100644 --- a/codes/multigrid/include/MultigridTaskReg.h +++ b/codes/multigrid/include/MultigridTaskReg.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/multigrid/src/BgField.cpp b/codes/multigrid/src/BgField.cpp index a83816a4..bb55ec65 100644 --- a/codes/multigrid/src/BgField.cpp +++ b/codes/multigrid/src/BgField.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/multigrid/src/BgGrid.cpp b/codes/multigrid/src/BgGrid.cpp index 919343b8..8aea1894 100644 --- a/codes/multigrid/src/BgGrid.cpp +++ b/codes/multigrid/src/BgGrid.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/multigrid/src/Multigrid.cpp b/codes/multigrid/src/Multigrid.cpp index 5700f501..687fa6e5 100644 --- a/codes/multigrid/src/Multigrid.cpp +++ b/codes/multigrid/src/Multigrid.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/multigrid/src/MultigridTaskReg.cpp b/codes/multigrid/src/MultigridTaskReg.cpp index ce0029a3..49539ade 100644 --- a/codes/multigrid/src/MultigridTaskReg.cpp +++ b/codes/multigrid/src/MultigridTaskReg.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/include/NsBcSolver.h b/codes/ns/include/NsBcSolver.h index 53ce7ba3..1cb3808c 100644 --- a/codes/ns/include/NsBcSolver.h +++ b/codes/ns/include/NsBcSolver.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/include/NsCom.h b/codes/ns/include/NsCom.h index 949101e1..feed437e 100644 --- a/codes/ns/include/NsCom.h +++ b/codes/ns/include/NsCom.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/include/NsCtrl.h b/codes/ns/include/NsCtrl.h index 12d44237..cbaf92d7 100644 --- a/codes/ns/include/NsCtrl.h +++ b/codes/ns/include/NsCtrl.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/include/NsIdx.h b/codes/ns/include/NsIdx.h index c6d16b95..bd5e2670 100644 --- a/codes/ns/include/NsIdx.h +++ b/codes/ns/include/NsIdx.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/include/NsInvFlux.h b/codes/ns/include/NsInvFlux.h index d068a801..c1c1b739 100644 --- a/codes/ns/include/NsInvFlux.h +++ b/codes/ns/include/NsInvFlux.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/include/NsLusgs.h b/codes/ns/include/NsLusgs.h index 277dd541..255f3ff0 100644 --- a/codes/ns/include/NsLusgs.h +++ b/codes/ns/include/NsLusgs.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/include/NsRestart.h b/codes/ns/include/NsRestart.h index eb7740b9..bf0345c9 100644 --- a/codes/ns/include/NsRestart.h +++ b/codes/ns/include/NsRestart.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/include/NsRhs.h b/codes/ns/include/NsRhs.h index 044f30de..508e071e 100644 --- a/codes/ns/include/NsRhs.h +++ b/codes/ns/include/NsRhs.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/include/NsSolver.h b/codes/ns/include/NsSolver.h index 79f68741..56c0c7aa 100644 --- a/codes/ns/include/NsSolver.h +++ b/codes/ns/include/NsSolver.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/include/NsSolverImp.h b/codes/ns/include/NsSolverImp.h index 932d481e..df9efc90 100644 --- a/codes/ns/include/NsSolverImp.h +++ b/codes/ns/include/NsSolverImp.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/include/NsSpectrum.h b/codes/ns/include/NsSpectrum.h index b8ca158d..70210912 100644 --- a/codes/ns/include/NsSpectrum.h +++ b/codes/ns/include/NsSpectrum.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/include/NsUnsteady.h b/codes/ns/include/NsUnsteady.h index 4cc66205..a609b5f1 100644 --- a/codes/ns/include/NsUnsteady.h +++ b/codes/ns/include/NsUnsteady.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/include/NsUpdate.h b/codes/ns/include/NsUpdate.h index 8fa26d0c..5353f720 100644 --- a/codes/ns/include/NsUpdate.h +++ b/codes/ns/include/NsUpdate.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/include/NsVisFlux.h b/codes/ns/include/NsVisFlux.h index e6277f64..fccd7ab8 100644 --- a/codes/ns/include/NsVisFlux.h +++ b/codes/ns/include/NsVisFlux.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/include/TimeStep.h b/codes/ns/include/TimeStep.h index d76404f0..32a6a6c7 100644 --- a/codes/ns/include/TimeStep.h +++ b/codes/ns/include/TimeStep.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/src/NsBcSolver.cpp b/codes/ns/src/NsBcSolver.cpp index 53b2ff0b..1b1e77ae 100644 --- a/codes/ns/src/NsBcSolver.cpp +++ b/codes/ns/src/NsBcSolver.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/src/NsCom.cpp b/codes/ns/src/NsCom.cpp index 4ec2bb1f..9078d82f 100644 --- a/codes/ns/src/NsCom.cpp +++ b/codes/ns/src/NsCom.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/src/NsCtrl.cpp b/codes/ns/src/NsCtrl.cpp index 43c3cefc..de757d4e 100644 --- a/codes/ns/src/NsCtrl.cpp +++ b/codes/ns/src/NsCtrl.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/src/NsInvFlux.cpp b/codes/ns/src/NsInvFlux.cpp index ddd8b25f..8ab75d9d 100644 --- a/codes/ns/src/NsInvFlux.cpp +++ b/codes/ns/src/NsInvFlux.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/src/NsLusgs.cpp b/codes/ns/src/NsLusgs.cpp index 2ec1ba60..d893d22d 100644 --- a/codes/ns/src/NsLusgs.cpp +++ b/codes/ns/src/NsLusgs.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/src/NsRestart.cpp b/codes/ns/src/NsRestart.cpp index 4e2e632f..3eacf4f9 100644 --- a/codes/ns/src/NsRestart.cpp +++ b/codes/ns/src/NsRestart.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/src/NsRhs.cpp b/codes/ns/src/NsRhs.cpp index 05133543..b582d187 100644 --- a/codes/ns/src/NsRhs.cpp +++ b/codes/ns/src/NsRhs.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/src/NsSolver.cpp b/codes/ns/src/NsSolver.cpp index 52578a66..5c7eced3 100644 --- a/codes/ns/src/NsSolver.cpp +++ b/codes/ns/src/NsSolver.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/src/NsSolverImp.cpp b/codes/ns/src/NsSolverImp.cpp index d0c94e01..db2d8d30 100644 --- a/codes/ns/src/NsSolverImp.cpp +++ b/codes/ns/src/NsSolverImp.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/src/NsSpectrum.cpp b/codes/ns/src/NsSpectrum.cpp index 071f6b77..c9e3615a 100644 --- a/codes/ns/src/NsSpectrum.cpp +++ b/codes/ns/src/NsSpectrum.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/src/NsUnsteady.cpp b/codes/ns/src/NsUnsteady.cpp index c5b32d19..3aeb976f 100644 --- a/codes/ns/src/NsUnsteady.cpp +++ b/codes/ns/src/NsUnsteady.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/src/NsUpdate.cpp b/codes/ns/src/NsUpdate.cpp index f19e37d1..8f83b322 100644 --- a/codes/ns/src/NsUpdate.cpp +++ b/codes/ns/src/NsUpdate.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/src/NsVisFlux.cpp b/codes/ns/src/NsVisFlux.cpp index e7a85d8a..47ce072c 100644 --- a/codes/ns/src/NsVisFlux.cpp +++ b/codes/ns/src/NsVisFlux.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/ns/src/TimeStep.cpp b/codes/ns/src/TimeStep.cpp index a5e35380..67a409d9 100644 --- a/codes/ns/src/TimeStep.cpp +++ b/codes/ns/src/TimeStep.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/parallel/include/BasicParallel.h b/codes/parallel/include/BasicParallel.h index d1eddc54..ddf6b8cb 100644 --- a/codes/parallel/include/BasicParallel.h +++ b/codes/parallel/include/BasicParallel.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/parallel/include/Parallel.h b/codes/parallel/include/Parallel.h index 79bac8d2..bcf78a2b 100644 --- a/codes/parallel/include/Parallel.h +++ b/codes/parallel/include/Parallel.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/parallel/src/BasicParallel.cpp b/codes/parallel/src/BasicParallel.cpp index 749af76d..9656ecf6 100644 --- a/codes/parallel/src/BasicParallel.cpp +++ b/codes/parallel/src/BasicParallel.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/parallel/src/Parallel.cpp b/codes/parallel/src/Parallel.cpp index 2ad2516f..3c70c690 100644 --- a/codes/parallel/src/Parallel.cpp +++ b/codes/parallel/src/Parallel.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/partition/include/Partition.h b/codes/partition/include/Partition.h index fcdba41f..88d2ed9d 100644 --- a/codes/partition/include/Partition.h +++ b/codes/partition/include/Partition.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/partition/include/SmartGrid.h b/codes/partition/include/SmartGrid.h index e621b456..7731116d 100644 --- a/codes/partition/include/SmartGrid.h +++ b/codes/partition/include/SmartGrid.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/partition/src/Partition.cpp b/codes/partition/src/Partition.cpp index b5ea45fe..4095cce0 100644 --- a/codes/partition/src/Partition.cpp +++ b/codes/partition/src/Partition.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/partition/src/SmartGrid.cpp b/codes/partition/src/SmartGrid.cpp index c150a9ff..cb749a3a 100644 --- a/codes/partition/src/SmartGrid.cpp +++ b/codes/partition/src/SmartGrid.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/physics/include/Atmosphere.h b/codes/physics/include/Atmosphere.h index 2f0ecf16..3e81414d 100644 --- a/codes/physics/include/Atmosphere.h +++ b/codes/physics/include/Atmosphere.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/physics/src/Atmosphere.cpp b/codes/physics/src/Atmosphere.cpp index 6a8d56a4..5b679bce 100644 --- a/codes/physics/src/Atmosphere.cpp +++ b/codes/physics/src/Atmosphere.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/postprocess/include/PostProcess.h b/codes/postprocess/include/PostProcess.h index 9b4fe4d4..0f82aeb3 100644 --- a/codes/postprocess/include/PostProcess.h +++ b/codes/postprocess/include/PostProcess.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/postprocess/src/PostProcess.cpp b/codes/postprocess/src/PostProcess.cpp index e4c6c27a..b3aea660 100644 --- a/codes/postprocess/src/PostProcess.cpp +++ b/codes/postprocess/src/PostProcess.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/project/include/Configure.h b/codes/project/include/Configure.h index 7f03c82f..5a9da9cb 100644 --- a/codes/project/include/Configure.h +++ b/codes/project/include/Configure.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/project/include/FileUtil.h b/codes/project/include/FileUtil.h index c382d87e..37b385c4 100644 --- a/codes/project/include/FileUtil.h +++ b/codes/project/include/FileUtil.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/project/include/OStream.h b/codes/project/include/OStream.h index fb26de11..d7477455 100644 --- a/codes/project/include/OStream.h +++ b/codes/project/include/OStream.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/project/include/Prj.h b/codes/project/include/Prj.h index a9dadc01..830f2b0c 100644 --- a/codes/project/include/Prj.h +++ b/codes/project/include/Prj.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/project/include/Stop.h b/codes/project/include/Stop.h index 90294eb0..822e6c30 100644 --- a/codes/project/include/Stop.h +++ b/codes/project/include/Stop.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/project/src/FileUtil.cpp b/codes/project/src/FileUtil.cpp index 8dcaca47..f34add3c 100644 --- a/codes/project/src/FileUtil.cpp +++ b/codes/project/src/FileUtil.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/project/src/OStream.cpp b/codes/project/src/OStream.cpp index 7bfd2bb3..062d12c0 100644 --- a/codes/project/src/OStream.cpp +++ b/codes/project/src/OStream.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/project/src/Prj.cpp b/codes/project/src/Prj.cpp index 21c49f1e..09495a1a 100644 --- a/codes/project/src/Prj.cpp +++ b/codes/project/src/Prj.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/project/src/Stop.cpp b/codes/project/src/Stop.cpp index 2f143359..42b6b627 100644 --- a/codes/project/src/Stop.cpp +++ b/codes/project/src/Stop.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/register/include/HXClone.h b/codes/register/include/HXClone.h index ebae759a..1030365d 100644 --- a/codes/register/include/HXClone.h +++ b/codes/register/include/HXClone.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/register/include/Message.h b/codes/register/include/Message.h index 83dde89b..0d5bd353 100644 --- a/codes/register/include/Message.h +++ b/codes/register/include/Message.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/register/include/MsgMapImp.h b/codes/register/include/MsgMapImp.h index 2e2110e6..fdc204da 100644 --- a/codes/register/include/MsgMapImp.h +++ b/codes/register/include/MsgMapImp.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/register/include/Register.h b/codes/register/include/Register.h index 13398d1e..ff01cbf2 100644 --- a/codes/register/include/Register.h +++ b/codes/register/include/Register.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/register/include/RegisterUtil.h b/codes/register/include/RegisterUtil.h index f00025fd..176729da 100644 --- a/codes/register/include/RegisterUtil.h +++ b/codes/register/include/RegisterUtil.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/register/src/HXClone.cpp b/codes/register/src/HXClone.cpp index 8aae6bea..5ee08831 100644 --- a/codes/register/src/HXClone.cpp +++ b/codes/register/src/HXClone.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/register/src/Message.cpp b/codes/register/src/Message.cpp index 35191768..1bc7f23e 100644 --- a/codes/register/src/Message.cpp +++ b/codes/register/src/Message.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/register/src/MsgMapImp.cpp b/codes/register/src/MsgMapImp.cpp index a8711bc2..2e254ca3 100644 --- a/codes/register/src/MsgMapImp.cpp +++ b/codes/register/src/MsgMapImp.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/register/src/Register.cpp b/codes/register/src/Register.cpp index 4c78d2c3..297eb3ae 100644 --- a/codes/register/src/Register.cpp +++ b/codes/register/src/Register.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/register/src/RegisterUtil.cpp b/codes/register/src/RegisterUtil.cpp index c030d666..8a393352 100644 --- a/codes/register/src/RegisterUtil.cpp +++ b/codes/register/src/RegisterUtil.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/residual/include/Residual.h b/codes/residual/include/Residual.h index 384ee91c..5c401659 100644 --- a/codes/residual/include/Residual.h +++ b/codes/residual/include/Residual.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/residual/include/ResidualTask.h b/codes/residual/include/ResidualTask.h index 186675ec..2f1d91bc 100644 --- a/codes/residual/include/ResidualTask.h +++ b/codes/residual/include/ResidualTask.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/residual/include/ResidualTaskReg.h b/codes/residual/include/ResidualTaskReg.h index 0601b306..6e7d681f 100644 --- a/codes/residual/include/ResidualTaskReg.h +++ b/codes/residual/include/ResidualTaskReg.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/residual/src/Residual.cpp b/codes/residual/src/Residual.cpp index ba523526..bfbba076 100644 --- a/codes/residual/src/Residual.cpp +++ b/codes/residual/src/Residual.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/residual/src/ResidualTask.cpp b/codes/residual/src/ResidualTask.cpp index c4d02f4d..a706fd37 100644 --- a/codes/residual/src/ResidualTask.cpp +++ b/codes/residual/src/ResidualTask.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/residual/src/ResidualTaskReg.cpp b/codes/residual/src/ResidualTaskReg.cpp index 68b991e9..777e89f6 100644 --- a/codes/residual/src/ResidualTaskReg.cpp +++ b/codes/residual/src/ResidualTaskReg.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/restart/include/Restart.h b/codes/restart/include/Restart.h index 7fad4c6b..4da5c636 100644 --- a/codes/restart/include/Restart.h +++ b/codes/restart/include/Restart.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/restart/include/RestartTaskReg.h b/codes/restart/include/RestartTaskReg.h index fbbe3426..94be3839 100644 --- a/codes/restart/include/RestartTaskReg.h +++ b/codes/restart/include/RestartTaskReg.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/restart/src/Restart.cpp b/codes/restart/src/Restart.cpp index 504a78f1..20c19f46 100644 --- a/codes/restart/src/Restart.cpp +++ b/codes/restart/src/Restart.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/restart/src/RestartTaskReg.cpp b/codes/restart/src/RestartTaskReg.cpp index 5b5d7845..9f9845f8 100644 --- a/codes/restart/src/RestartTaskReg.cpp +++ b/codes/restart/src/RestartTaskReg.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/include/Blasius.h b/codes/scalar/include/Blasius.h index 5614970f..3026e62f 100644 --- a/codes/scalar/include/Blasius.h +++ b/codes/scalar/include/Blasius.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/include/FieldPara.h b/codes/scalar/include/FieldPara.h index 1b75866f..c76298f5 100644 --- a/codes/scalar/include/FieldPara.h +++ b/codes/scalar/include/FieldPara.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/include/FieldSolver.h b/codes/scalar/include/FieldSolver.h index 33663fa9..32a05694 100644 --- a/codes/scalar/include/FieldSolver.h +++ b/codes/scalar/include/FieldSolver.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/include/FieldSolverBasic.h b/codes/scalar/include/FieldSolverBasic.h index 1ceab7f2..775ea428 100644 --- a/codes/scalar/include/FieldSolverBasic.h +++ b/codes/scalar/include/FieldSolverBasic.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/include/FieldSolverCuda.h b/codes/scalar/include/FieldSolverCuda.h index b78c481d..28a9eb7d 100644 --- a/codes/scalar/include/FieldSolverCuda.h +++ b/codes/scalar/include/FieldSolverCuda.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/include/FieldSolverOpenMP.h b/codes/scalar/include/FieldSolverOpenMP.h index 7a052612..43f4e044 100644 --- a/codes/scalar/include/FieldSolverOpenMP.h +++ b/codes/scalar/include/FieldSolverOpenMP.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/include/MetisGrid.h b/codes/scalar/include/MetisGrid.h index e8bd90ee..5a3100f8 100644 --- a/codes/scalar/include/MetisGrid.h +++ b/codes/scalar/include/MetisGrid.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/include/Numpy.h b/codes/scalar/include/Numpy.h index f9e5d03e..c9f50d70 100644 --- a/codes/scalar/include/Numpy.h +++ b/codes/scalar/include/Numpy.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/include/Scalar.h b/codes/scalar/include/Scalar.h index e0716608..bb91a614 100644 --- a/codes/scalar/include/Scalar.h +++ b/codes/scalar/include/Scalar.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/include/ScalarAlloc.h b/codes/scalar/include/ScalarAlloc.h index e48a71db..7ba2fcc7 100644 --- a/codes/scalar/include/ScalarAlloc.h +++ b/codes/scalar/include/ScalarAlloc.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/include/ScalarCgns.h b/codes/scalar/include/ScalarCgns.h index 921dd934..9c185291 100644 --- a/codes/scalar/include/ScalarCgns.h +++ b/codes/scalar/include/ScalarCgns.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/include/ScalarDataIO.h b/codes/scalar/include/ScalarDataIO.h index 137947b4..18bc0ff3 100644 --- a/codes/scalar/include/ScalarDataIO.h +++ b/codes/scalar/include/ScalarDataIO.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/include/ScalarField.h b/codes/scalar/include/ScalarField.h index a15477fb..6fcbd475 100644 --- a/codes/scalar/include/ScalarField.h +++ b/codes/scalar/include/ScalarField.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/include/ScalarFieldRecord.h b/codes/scalar/include/ScalarFieldRecord.h index 9908baea..be5925f4 100644 --- a/codes/scalar/include/ScalarFieldRecord.h +++ b/codes/scalar/include/ScalarFieldRecord.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/include/ScalarGrid.h b/codes/scalar/include/ScalarGrid.h index ce2f25d7..2534c995 100644 --- a/codes/scalar/include/ScalarGrid.h +++ b/codes/scalar/include/ScalarGrid.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/include/ScalarIFace.h b/codes/scalar/include/ScalarIFace.h index dd15cd45..ee789f82 100644 --- a/codes/scalar/include/ScalarIFace.h +++ b/codes/scalar/include/ScalarIFace.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/include/ScalarMetis.h b/codes/scalar/include/ScalarMetis.h index 61149bc5..c76dda62 100644 --- a/codes/scalar/include/ScalarMetis.h +++ b/codes/scalar/include/ScalarMetis.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/include/ScalarOrder.h b/codes/scalar/include/ScalarOrder.h index cbae1d91..b8a4e5e8 100644 --- a/codes/scalar/include/ScalarOrder.h +++ b/codes/scalar/include/ScalarOrder.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/include/ScalarSolver.h b/codes/scalar/include/ScalarSolver.h index 2fbab427..f852b534 100644 --- a/codes/scalar/include/ScalarSolver.h +++ b/codes/scalar/include/ScalarSolver.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/include/ScalarZone.h b/codes/scalar/include/ScalarZone.h index 473180d6..919b5eb1 100644 --- a/codes/scalar/include/ScalarZone.h +++ b/codes/scalar/include/ScalarZone.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/include/SolverDevice.h b/codes/scalar/include/SolverDevice.h index 5b09b275..b4c70e0b 100644 --- a/codes/scalar/include/SolverDevice.h +++ b/codes/scalar/include/SolverDevice.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/src/Blasius.cpp b/codes/scalar/src/Blasius.cpp index 7f489d50..a16a4fcd 100644 --- a/codes/scalar/src/Blasius.cpp +++ b/codes/scalar/src/Blasius.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/src/FieldPara.cpp b/codes/scalar/src/FieldPara.cpp index 2852f6d5..1c40ddcb 100644 --- a/codes/scalar/src/FieldPara.cpp +++ b/codes/scalar/src/FieldPara.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/src/FieldSolver.cpp b/codes/scalar/src/FieldSolver.cpp index 2571e20a..539f559c 100644 --- a/codes/scalar/src/FieldSolver.cpp +++ b/codes/scalar/src/FieldSolver.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/src/FieldSolverBasic.cpp b/codes/scalar/src/FieldSolverBasic.cpp index 306b08cb..cbc45751 100644 --- a/codes/scalar/src/FieldSolverBasic.cpp +++ b/codes/scalar/src/FieldSolverBasic.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/src/FieldSolverCuda.cpp b/codes/scalar/src/FieldSolverCuda.cpp index d02582be..e6e773cb 100644 --- a/codes/scalar/src/FieldSolverCuda.cpp +++ b/codes/scalar/src/FieldSolverCuda.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/src/FieldSolverOpenMP.cpp b/codes/scalar/src/FieldSolverOpenMP.cpp index 7be3f4ad..5e69667b 100644 --- a/codes/scalar/src/FieldSolverOpenMP.cpp +++ b/codes/scalar/src/FieldSolverOpenMP.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/src/MetisGrid.cpp b/codes/scalar/src/MetisGrid.cpp index 5c3a1476..097cef57 100644 --- a/codes/scalar/src/MetisGrid.cpp +++ b/codes/scalar/src/MetisGrid.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/src/Numpy.cpp b/codes/scalar/src/Numpy.cpp index 37cf9d2e..ff9d0cf9 100644 --- a/codes/scalar/src/Numpy.cpp +++ b/codes/scalar/src/Numpy.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/src/Scalar.cpp b/codes/scalar/src/Scalar.cpp index d4868f1d..2c773a18 100644 --- a/codes/scalar/src/Scalar.cpp +++ b/codes/scalar/src/Scalar.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/src/ScalarAlloc.cpp b/codes/scalar/src/ScalarAlloc.cpp index bb32681d..735a1510 100644 --- a/codes/scalar/src/ScalarAlloc.cpp +++ b/codes/scalar/src/ScalarAlloc.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/src/ScalarCgns.cpp b/codes/scalar/src/ScalarCgns.cpp index 84475991..17e51363 100644 --- a/codes/scalar/src/ScalarCgns.cpp +++ b/codes/scalar/src/ScalarCgns.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/src/ScalarDataIO.cpp b/codes/scalar/src/ScalarDataIO.cpp index 0023ad75..f656a6f4 100644 --- a/codes/scalar/src/ScalarDataIO.cpp +++ b/codes/scalar/src/ScalarDataIO.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/src/ScalarField.cpp b/codes/scalar/src/ScalarField.cpp index 455dc1fa..c3f64bf9 100644 --- a/codes/scalar/src/ScalarField.cpp +++ b/codes/scalar/src/ScalarField.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/src/ScalarFieldRecord.cpp b/codes/scalar/src/ScalarFieldRecord.cpp index 949fc30a..0073ab89 100644 --- a/codes/scalar/src/ScalarFieldRecord.cpp +++ b/codes/scalar/src/ScalarFieldRecord.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/src/ScalarGrid.cpp b/codes/scalar/src/ScalarGrid.cpp index 0be346bc..8b62ab91 100644 --- a/codes/scalar/src/ScalarGrid.cpp +++ b/codes/scalar/src/ScalarGrid.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/src/ScalarIFace.cpp b/codes/scalar/src/ScalarIFace.cpp index 8f446da1..c943ccd5 100644 --- a/codes/scalar/src/ScalarIFace.cpp +++ b/codes/scalar/src/ScalarIFace.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/src/ScalarMetis.cpp b/codes/scalar/src/ScalarMetis.cpp index a7dbf7a8..7e5415d2 100644 --- a/codes/scalar/src/ScalarMetis.cpp +++ b/codes/scalar/src/ScalarMetis.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/src/ScalarOrder.cpp b/codes/scalar/src/ScalarOrder.cpp index ce013218..b1d90383 100644 --- a/codes/scalar/src/ScalarOrder.cpp +++ b/codes/scalar/src/ScalarOrder.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/src/ScalarSolver.cpp b/codes/scalar/src/ScalarSolver.cpp index 6f61720b..080d7314 100644 --- a/codes/scalar/src/ScalarSolver.cpp +++ b/codes/scalar/src/ScalarSolver.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/scalar/src/ScalarZone.cpp b/codes/scalar/src/ScalarZone.cpp index 6f0ac08e..1996d5a1 100644 --- a/codes/scalar/src/ScalarZone.cpp +++ b/codes/scalar/src/ScalarZone.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/include/BcData.h b/codes/solver/include/BcData.h index 797849ed..0c6bb98b 100644 --- a/codes/solver/include/BcData.h +++ b/codes/solver/include/BcData.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/include/BcSolver.h b/codes/solver/include/BcSolver.h index 00c6b816..bfbec6b0 100644 --- a/codes/solver/include/BcSolver.h +++ b/codes/solver/include/BcSolver.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/include/Converge.h b/codes/solver/include/Converge.h index b99e5f3b..8caec5e8 100644 --- a/codes/solver/include/Converge.h +++ b/codes/solver/include/Converge.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/include/FieldTaskReg.h b/codes/solver/include/FieldTaskReg.h index 03894d5a..56cfa213 100644 --- a/codes/solver/include/FieldTaskReg.h +++ b/codes/solver/include/FieldTaskReg.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/include/FieldWrap.h b/codes/solver/include/FieldWrap.h index 9cedd528..64405643 100644 --- a/codes/solver/include/FieldWrap.h +++ b/codes/solver/include/FieldWrap.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/include/Rhs.h b/codes/solver/include/Rhs.h index 20a49907..79a8d409 100644 --- a/codes/solver/include/Rhs.h +++ b/codes/solver/include/Rhs.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/include/Solver.h b/codes/solver/include/Solver.h index 0cb214a3..88d228b1 100644 --- a/codes/solver/include/Solver.h +++ b/codes/solver/include/Solver.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/include/SolverImp.h b/codes/solver/include/SolverImp.h index ae961572..70299e42 100644 --- a/codes/solver/include/SolverImp.h +++ b/codes/solver/include/SolverImp.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/include/SolverInfo.h b/codes/solver/include/SolverInfo.h index cc7b5a6d..9e5cb84a 100644 --- a/codes/solver/include/SolverInfo.h +++ b/codes/solver/include/SolverInfo.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/include/SolverMap.h b/codes/solver/include/SolverMap.h index 7b4f9306..fa828928 100644 --- a/codes/solver/include/SolverMap.h +++ b/codes/solver/include/SolverMap.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/include/SolverRegData.h b/codes/solver/include/SolverRegData.h index 5d727e2c..c1a40d56 100644 --- a/codes/solver/include/SolverRegData.h +++ b/codes/solver/include/SolverRegData.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/include/SolverState.h b/codes/solver/include/SolverState.h index 8145ab98..2504b3b6 100644 --- a/codes/solver/include/SolverState.h +++ b/codes/solver/include/SolverState.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/include/Stress.h b/codes/solver/include/Stress.h index 01bc75ca..0d2ef6d8 100644 --- a/codes/solver/include/Stress.h +++ b/codes/solver/include/Stress.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/include/TimeIntegral.h b/codes/solver/include/TimeIntegral.h index 32965e68..40c77ed0 100644 --- a/codes/solver/include/TimeIntegral.h +++ b/codes/solver/include/TimeIntegral.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/src/BcData.cpp b/codes/solver/src/BcData.cpp index 9814c05a..6688cd04 100644 --- a/codes/solver/src/BcData.cpp +++ b/codes/solver/src/BcData.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/src/BcSolver.cpp b/codes/solver/src/BcSolver.cpp index f401e81f..8dbdc0b2 100644 --- a/codes/solver/src/BcSolver.cpp +++ b/codes/solver/src/BcSolver.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/src/Converge.cpp b/codes/solver/src/Converge.cpp index 1461cf09..7329cbb2 100644 --- a/codes/solver/src/Converge.cpp +++ b/codes/solver/src/Converge.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/src/FieldTaskReg.cpp b/codes/solver/src/FieldTaskReg.cpp index 42682567..616baf56 100644 --- a/codes/solver/src/FieldTaskReg.cpp +++ b/codes/solver/src/FieldTaskReg.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/src/FieldWrap.cpp b/codes/solver/src/FieldWrap.cpp index 8ebc70b2..389bca41 100644 --- a/codes/solver/src/FieldWrap.cpp +++ b/codes/solver/src/FieldWrap.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/src/Rhs.cpp b/codes/solver/src/Rhs.cpp index 85b186bd..cd65cf0b 100644 --- a/codes/solver/src/Rhs.cpp +++ b/codes/solver/src/Rhs.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/src/Solver.cpp b/codes/solver/src/Solver.cpp index bc2954d9..9c1d5fe0 100644 --- a/codes/solver/src/Solver.cpp +++ b/codes/solver/src/Solver.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/src/SolverImp.cpp b/codes/solver/src/SolverImp.cpp index 56f9a1ef..1b7b9d6b 100644 --- a/codes/solver/src/SolverImp.cpp +++ b/codes/solver/src/SolverImp.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/src/SolverInfo.cpp b/codes/solver/src/SolverInfo.cpp index f353ad59..d71c47e1 100644 --- a/codes/solver/src/SolverInfo.cpp +++ b/codes/solver/src/SolverInfo.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/src/SolverMap.cpp b/codes/solver/src/SolverMap.cpp index 0c624413..e8a5c87f 100644 --- a/codes/solver/src/SolverMap.cpp +++ b/codes/solver/src/SolverMap.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/src/SolverRegData.cpp b/codes/solver/src/SolverRegData.cpp index 9417fe54..6368385a 100644 --- a/codes/solver/src/SolverRegData.cpp +++ b/codes/solver/src/SolverRegData.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/src/SolverState.cpp b/codes/solver/src/SolverState.cpp index c0df2102..ac9d96f0 100644 --- a/codes/solver/src/SolverState.cpp +++ b/codes/solver/src/SolverState.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/src/Stress.cpp b/codes/solver/src/Stress.cpp index 38ff2299..5bb114e8 100644 --- a/codes/solver/src/Stress.cpp +++ b/codes/solver/src/Stress.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/solver/src/TimeIntegral.cpp b/codes/solver/src/TimeIntegral.cpp index d46cc289..ed449907 100644 --- a/codes/solver/src/TimeIntegral.cpp +++ b/codes/solver/src/TimeIntegral.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/BlkMesh.h b/codes/special/include/BlkMesh.h index 4f892a08..c899c6ae 100644 --- a/codes/special/include/BlkMesh.h +++ b/codes/special/include/BlkMesh.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/Block2D.h b/codes/special/include/Block2D.h index 20b0a19c..1a20fe11 100644 --- a/codes/special/include/Block2D.h +++ b/codes/special/include/Block2D.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/Block3D.h b/codes/special/include/Block3D.h index 43543f21..551437dd 100644 --- a/codes/special/include/Block3D.h +++ b/codes/special/include/Block3D.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/BlockElem.h b/codes/special/include/BlockElem.h index 44001ab9..ba636bdc 100644 --- a/codes/special/include/BlockElem.h +++ b/codes/special/include/BlockElem.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/BlockFaceSolver.h b/codes/special/include/BlockFaceSolver.h index a20bdab4..015cdb4b 100644 --- a/codes/special/include/BlockFaceSolver.h +++ b/codes/special/include/BlockFaceSolver.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/BlockMachine.h b/codes/special/include/BlockMachine.h index ce48ba6a..c0378d42 100644 --- a/codes/special/include/BlockMachine.h +++ b/codes/special/include/BlockMachine.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/CalcCoor.h b/codes/special/include/CalcCoor.h index 2b6e529a..4b0291e5 100644 --- a/codes/special/include/CalcCoor.h +++ b/codes/special/include/CalcCoor.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/Cavity.h b/codes/special/include/Cavity.h index c8b2ec15..3142c0e0 100644 --- a/codes/special/include/Cavity.h +++ b/codes/special/include/Cavity.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/CgnsTest.h b/codes/special/include/CgnsTest.h index c3c08fb5..5ba740a4 100644 --- a/codes/special/include/CgnsTest.h +++ b/codes/special/include/CgnsTest.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/CgnsTestTmp.h b/codes/special/include/CgnsTestTmp.h index 17c974c9..6875588e 100644 --- a/codes/special/include/CgnsTestTmp.h +++ b/codes/special/include/CgnsTestTmp.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/CircleInfo.h b/codes/special/include/CircleInfo.h index e977c55e..d4097f51 100644 --- a/codes/special/include/CircleInfo.h +++ b/codes/special/include/CircleInfo.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/CircleLineMesh.h b/codes/special/include/CircleLineMesh.h index 7babc600..30b41a72 100644 --- a/codes/special/include/CircleLineMesh.h +++ b/codes/special/include/CircleLineMesh.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/ClassicGrid.h b/codes/special/include/ClassicGrid.h index 39b045c4..6b0d140f 100644 --- a/codes/special/include/ClassicGrid.h +++ b/codes/special/include/ClassicGrid.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/CurveInfo.h b/codes/special/include/CurveInfo.h index 21eeb9b6..bf136059 100644 --- a/codes/special/include/CurveInfo.h +++ b/codes/special/include/CurveInfo.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/CurveLine.h b/codes/special/include/CurveLine.h index 271c3f67..94544f2e 100644 --- a/codes/special/include/CurveLine.h +++ b/codes/special/include/CurveLine.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/CurveMachine.h b/codes/special/include/CurveMachine.h index 5e70c425..78b150e0 100644 --- a/codes/special/include/CurveMachine.h +++ b/codes/special/include/CurveMachine.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/CurveMesh.h b/codes/special/include/CurveMesh.h index 17f73d4e..545414f8 100644 --- a/codes/special/include/CurveMesh.h +++ b/codes/special/include/CurveMesh.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/Cylinder.h b/codes/special/include/Cylinder.h index 573bcefc..baed4eff 100644 --- a/codes/special/include/Cylinder.h +++ b/codes/special/include/Cylinder.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/DomainMachine.h b/codes/special/include/DomainMachine.h index fc19bc15..61a85991 100644 --- a/codes/special/include/DomainMachine.h +++ b/codes/special/include/DomainMachine.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/GridCreate.h b/codes/special/include/GridCreate.h index ab22a728..9de245f4 100644 --- a/codes/special/include/GridCreate.h +++ b/codes/special/include/GridCreate.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/GridMachine.h b/codes/special/include/GridMachine.h index 22eca26d..8e91de2a 100644 --- a/codes/special/include/GridMachine.h +++ b/codes/special/include/GridMachine.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/LineInfo.h b/codes/special/include/LineInfo.h index 566c6d39..1ef15387 100644 --- a/codes/special/include/LineInfo.h +++ b/codes/special/include/LineInfo.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/LineMachine.h b/codes/special/include/LineMachine.h index d100b1cb..11d9631e 100644 --- a/codes/special/include/LineMachine.h +++ b/codes/special/include/LineMachine.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/LineMesh.h b/codes/special/include/LineMesh.h index 20ffb35d..4ad593cc 100644 --- a/codes/special/include/LineMesh.h +++ b/codes/special/include/LineMesh.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/LineMeshImp.h b/codes/special/include/LineMeshImp.h index 26de3989..95e729b5 100644 --- a/codes/special/include/LineMeshImp.h +++ b/codes/special/include/LineMeshImp.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/MDomain.h b/codes/special/include/MDomain.h index bfa54d9f..ed3ee37c 100644 --- a/codes/special/include/MDomain.h +++ b/codes/special/include/MDomain.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/MLine.h b/codes/special/include/MLine.h index dd981793..e8c549f7 100644 --- a/codes/special/include/MLine.h +++ b/codes/special/include/MLine.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/Mesh.h b/codes/special/include/Mesh.h index 0b97563c..9e60ed4c 100644 --- a/codes/special/include/Mesh.h +++ b/codes/special/include/Mesh.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/MpiTest.h b/codes/special/include/MpiTest.h index 1ad15da6..6868d6a3 100644 --- a/codes/special/include/MpiTest.h +++ b/codes/special/include/MpiTest.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/PointMachine.h b/codes/special/include/PointMachine.h index 7503388d..09d5386c 100644 --- a/codes/special/include/PointMachine.h +++ b/codes/special/include/PointMachine.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/Rae2822.h b/codes/special/include/Rae2822.h index ab4bf8a1..6e76ea14 100644 --- a/codes/special/include/Rae2822.h +++ b/codes/special/include/Rae2822.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/SDomain.h b/codes/special/include/SDomain.h index 0bc02128..9fbadad8 100644 --- a/codes/special/include/SDomain.h +++ b/codes/special/include/SDomain.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/SegmentCtrl.h b/codes/special/include/SegmentCtrl.h index 58c10a3d..366540d7 100644 --- a/codes/special/include/SegmentCtrl.h +++ b/codes/special/include/SegmentCtrl.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/SimpleDomain.h b/codes/special/include/SimpleDomain.h index 8e2b3230..875423fd 100644 --- a/codes/special/include/SimpleDomain.h +++ b/codes/special/include/SimpleDomain.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/StrBcSetting.h b/codes/special/include/StrBcSetting.h index eee47db9..79fa3661 100644 --- a/codes/special/include/StrBcSetting.h +++ b/codes/special/include/StrBcSetting.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/include/Transfinite.h b/codes/special/include/Transfinite.h index a807dce8..73334580 100644 --- a/codes/special/include/Transfinite.h +++ b/codes/special/include/Transfinite.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/BlkMesh.cpp b/codes/special/src/BlkMesh.cpp index adb711d3..9aaeeb27 100644 --- a/codes/special/src/BlkMesh.cpp +++ b/codes/special/src/BlkMesh.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/Block2D.cpp b/codes/special/src/Block2D.cpp index 163f5037..365e4b8a 100644 --- a/codes/special/src/Block2D.cpp +++ b/codes/special/src/Block2D.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/Block3D.cpp b/codes/special/src/Block3D.cpp index 59777ecd..bd9fe9e8 100644 --- a/codes/special/src/Block3D.cpp +++ b/codes/special/src/Block3D.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/BlockElem.cpp b/codes/special/src/BlockElem.cpp index da513aa4..2affd7f4 100644 --- a/codes/special/src/BlockElem.cpp +++ b/codes/special/src/BlockElem.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/BlockFaceSolver.cpp b/codes/special/src/BlockFaceSolver.cpp index 1e588148..106fe910 100644 --- a/codes/special/src/BlockFaceSolver.cpp +++ b/codes/special/src/BlockFaceSolver.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/BlockMachine.cpp b/codes/special/src/BlockMachine.cpp index c4f7dfd8..dec35039 100644 --- a/codes/special/src/BlockMachine.cpp +++ b/codes/special/src/BlockMachine.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/CalcCoor.cpp b/codes/special/src/CalcCoor.cpp index 8f17112c..d185f5a8 100644 --- a/codes/special/src/CalcCoor.cpp +++ b/codes/special/src/CalcCoor.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/Cavity.cpp b/codes/special/src/Cavity.cpp index f78909cb..3c18ea1c 100644 --- a/codes/special/src/Cavity.cpp +++ b/codes/special/src/Cavity.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/CgnsTest.cpp b/codes/special/src/CgnsTest.cpp index 599a55a9..f97e0e49 100644 --- a/codes/special/src/CgnsTest.cpp +++ b/codes/special/src/CgnsTest.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/CgnsTestTmp.cpp b/codes/special/src/CgnsTestTmp.cpp index 27084abe..1f80c052 100644 --- a/codes/special/src/CgnsTestTmp.cpp +++ b/codes/special/src/CgnsTestTmp.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/CircleInfo.cpp b/codes/special/src/CircleInfo.cpp index 5574e93c..a84ce6a6 100644 --- a/codes/special/src/CircleInfo.cpp +++ b/codes/special/src/CircleInfo.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/CircleLineMesh.cpp b/codes/special/src/CircleLineMesh.cpp index ee4f7d08..fd19bd48 100644 --- a/codes/special/src/CircleLineMesh.cpp +++ b/codes/special/src/CircleLineMesh.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/ClassicGrid.cpp b/codes/special/src/ClassicGrid.cpp index 2e587c80..49f5c2e8 100644 --- a/codes/special/src/ClassicGrid.cpp +++ b/codes/special/src/ClassicGrid.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/CurveInfo.cpp b/codes/special/src/CurveInfo.cpp index 549ca30f..90da9977 100644 --- a/codes/special/src/CurveInfo.cpp +++ b/codes/special/src/CurveInfo.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/CurveLine.cpp b/codes/special/src/CurveLine.cpp index d8324520..5bcf4db1 100644 --- a/codes/special/src/CurveLine.cpp +++ b/codes/special/src/CurveLine.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/CurveMachine.cpp b/codes/special/src/CurveMachine.cpp index d358d180..ac2f95fa 100644 --- a/codes/special/src/CurveMachine.cpp +++ b/codes/special/src/CurveMachine.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/CurveMesh.cpp b/codes/special/src/CurveMesh.cpp index 86984e7a..2e4b4da2 100644 --- a/codes/special/src/CurveMesh.cpp +++ b/codes/special/src/CurveMesh.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/Cylinder.cpp b/codes/special/src/Cylinder.cpp index fbed9180..57d36191 100644 --- a/codes/special/src/Cylinder.cpp +++ b/codes/special/src/Cylinder.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/DomainMachine.cpp b/codes/special/src/DomainMachine.cpp index 9474863b..db2ea771 100644 --- a/codes/special/src/DomainMachine.cpp +++ b/codes/special/src/DomainMachine.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/GridCreate.cpp b/codes/special/src/GridCreate.cpp index 31d2f76f..2e0eacf8 100644 --- a/codes/special/src/GridCreate.cpp +++ b/codes/special/src/GridCreate.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/GridMachine.cpp b/codes/special/src/GridMachine.cpp index 93d7d128..41d26a0f 100644 --- a/codes/special/src/GridMachine.cpp +++ b/codes/special/src/GridMachine.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/LineInfo.cpp b/codes/special/src/LineInfo.cpp index c8c2ae49..6df2d02c 100644 --- a/codes/special/src/LineInfo.cpp +++ b/codes/special/src/LineInfo.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/LineMachine.cpp b/codes/special/src/LineMachine.cpp index 337c33e2..fe21a74a 100644 --- a/codes/special/src/LineMachine.cpp +++ b/codes/special/src/LineMachine.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/LineMesh.cpp b/codes/special/src/LineMesh.cpp index 479ec1fb..671efa48 100644 --- a/codes/special/src/LineMesh.cpp +++ b/codes/special/src/LineMesh.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/LineMeshImp.cpp b/codes/special/src/LineMeshImp.cpp index a3d7f2ef..d66cb42e 100644 --- a/codes/special/src/LineMeshImp.cpp +++ b/codes/special/src/LineMeshImp.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/MDomain.cpp b/codes/special/src/MDomain.cpp index c6df8d02..61cf45d0 100644 --- a/codes/special/src/MDomain.cpp +++ b/codes/special/src/MDomain.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/MLine.cpp b/codes/special/src/MLine.cpp index d1caa410..f35b5e05 100644 --- a/codes/special/src/MLine.cpp +++ b/codes/special/src/MLine.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/Mesh.cpp b/codes/special/src/Mesh.cpp index a760182b..36de7944 100644 --- a/codes/special/src/Mesh.cpp +++ b/codes/special/src/Mesh.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/MpiTest.cpp b/codes/special/src/MpiTest.cpp index 42c16892..3868fff6 100644 --- a/codes/special/src/MpiTest.cpp +++ b/codes/special/src/MpiTest.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/PointMachine.cpp b/codes/special/src/PointMachine.cpp index c1675072..a7a39d39 100644 --- a/codes/special/src/PointMachine.cpp +++ b/codes/special/src/PointMachine.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/Rae2822.cpp b/codes/special/src/Rae2822.cpp index 4f70a407..1b86e4d8 100644 --- a/codes/special/src/Rae2822.cpp +++ b/codes/special/src/Rae2822.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/SDomain.cpp b/codes/special/src/SDomain.cpp index b16de12e..d6173658 100644 --- a/codes/special/src/SDomain.cpp +++ b/codes/special/src/SDomain.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/SegmentCtrl.cpp b/codes/special/src/SegmentCtrl.cpp index dbe45441..9373fdb1 100644 --- a/codes/special/src/SegmentCtrl.cpp +++ b/codes/special/src/SegmentCtrl.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/SimpleDomain.cpp b/codes/special/src/SimpleDomain.cpp index 1ffcda63..ddc20f1b 100644 --- a/codes/special/src/SimpleDomain.cpp +++ b/codes/special/src/SimpleDomain.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/StrBcSetting.cpp b/codes/special/src/StrBcSetting.cpp index 65532b89..ec86db21 100644 --- a/codes/special/src/StrBcSetting.cpp +++ b/codes/special/src/StrBcSetting.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/special/src/Transfinite.cpp b/codes/special/src/Transfinite.cpp index 27bf951b..d7e86d62 100644 --- a/codes/special/src/Transfinite.cpp +++ b/codes/special/src/Transfinite.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/include/ActionMap.h b/codes/task/include/ActionMap.h index 586ed55b..ff66613d 100644 --- a/codes/task/include/ActionMap.h +++ b/codes/task/include/ActionMap.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/include/ActionState.h b/codes/task/include/ActionState.h index 1efbc76e..8d10da34 100644 --- a/codes/task/include/ActionState.h +++ b/codes/task/include/ActionState.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/include/Category.h b/codes/task/include/Category.h index 6eeb5c9a..27285e49 100644 --- a/codes/task/include/Category.h +++ b/codes/task/include/Category.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/include/CmxTask.h b/codes/task/include/CmxTask.h index 94e41c78..de9bcd38 100644 --- a/codes/task/include/CmxTask.h +++ b/codes/task/include/CmxTask.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/include/Command.h b/codes/task/include/Command.h index 9a3ba4e7..6b8e44e6 100644 --- a/codes/task/include/Command.h +++ b/codes/task/include/Command.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/include/FileInfo.h b/codes/task/include/FileInfo.h index 8b481ccf..5ad05625 100644 --- a/codes/task/include/FileInfo.h +++ b/codes/task/include/FileInfo.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/include/GridState.h b/codes/task/include/GridState.h index 17ea7c5b..d8879ca9 100644 --- a/codes/task/include/GridState.h +++ b/codes/task/include/GridState.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/include/InterfaceTask.h b/codes/task/include/InterfaceTask.h index 7b9e4e57..58f71faf 100644 --- a/codes/task/include/InterfaceTask.h +++ b/codes/task/include/InterfaceTask.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/include/OversetTask.h b/codes/task/include/OversetTask.h index 43a6363a..f684644e 100644 --- a/codes/task/include/OversetTask.h +++ b/codes/task/include/OversetTask.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/include/ReadTask.h b/codes/task/include/ReadTask.h index 182b2ae6..a49095ed 100644 --- a/codes/task/include/ReadTask.h +++ b/codes/task/include/ReadTask.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/include/SimpleTask.h b/codes/task/include/SimpleTask.h index 7e842b12..b297e923 100644 --- a/codes/task/include/SimpleTask.h +++ b/codes/task/include/SimpleTask.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/include/State.h b/codes/task/include/State.h index 6ab584bf..9ba64d0a 100644 --- a/codes/task/include/State.h +++ b/codes/task/include/State.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/include/Task.h b/codes/task/include/Task.h index 39cd07c5..7363ecab 100644 --- a/codes/task/include/Task.h +++ b/codes/task/include/Task.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/include/TaskCom.h b/codes/task/include/TaskCom.h index dd40940d..f5e86b68 100644 --- a/codes/task/include/TaskCom.h +++ b/codes/task/include/TaskCom.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/include/TaskImp.h b/codes/task/include/TaskImp.h index 0a88830f..0998ca49 100644 --- a/codes/task/include/TaskImp.h +++ b/codes/task/include/TaskImp.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/include/TaskRegister.h b/codes/task/include/TaskRegister.h index b9ee15cf..0a60a079 100644 --- a/codes/task/include/TaskRegister.h +++ b/codes/task/include/TaskRegister.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/include/TaskState.h b/codes/task/include/TaskState.h index 2c849f93..fcbaf52d 100644 --- a/codes/task/include/TaskState.h +++ b/codes/task/include/TaskState.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/include/WriteTask.h b/codes/task/include/WriteTask.h index 8fb768a6..0c05d22a 100644 --- a/codes/task/include/WriteTask.h +++ b/codes/task/include/WriteTask.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/src/ActionMap.cpp b/codes/task/src/ActionMap.cpp index e929655b..6f4619c9 100644 --- a/codes/task/src/ActionMap.cpp +++ b/codes/task/src/ActionMap.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/src/ActionState.cpp b/codes/task/src/ActionState.cpp index 490c06af..6a09b260 100644 --- a/codes/task/src/ActionState.cpp +++ b/codes/task/src/ActionState.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/src/Category.cpp b/codes/task/src/Category.cpp index b795aff9..b763c308 100644 --- a/codes/task/src/Category.cpp +++ b/codes/task/src/Category.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/src/CmxTask.cpp b/codes/task/src/CmxTask.cpp index 65e011fc..a838c2e6 100644 --- a/codes/task/src/CmxTask.cpp +++ b/codes/task/src/CmxTask.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/src/Command.cpp b/codes/task/src/Command.cpp index 284e166c..f28ac0f3 100644 --- a/codes/task/src/Command.cpp +++ b/codes/task/src/Command.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/src/FileInfo.cpp b/codes/task/src/FileInfo.cpp index e93d1bd8..9eb0fce2 100644 --- a/codes/task/src/FileInfo.cpp +++ b/codes/task/src/FileInfo.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/src/GridState.cpp b/codes/task/src/GridState.cpp index b4eb63e1..859c212e 100644 --- a/codes/task/src/GridState.cpp +++ b/codes/task/src/GridState.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/src/InterfaceTask.cpp b/codes/task/src/InterfaceTask.cpp index e7c31109..7245cf0e 100644 --- a/codes/task/src/InterfaceTask.cpp +++ b/codes/task/src/InterfaceTask.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/src/OversetTask.cpp b/codes/task/src/OversetTask.cpp index 56c578e6..33d68335 100644 --- a/codes/task/src/OversetTask.cpp +++ b/codes/task/src/OversetTask.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/src/ReadTask.cpp b/codes/task/src/ReadTask.cpp index 62bf0d2c..05a657a6 100644 --- a/codes/task/src/ReadTask.cpp +++ b/codes/task/src/ReadTask.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/src/SimpleTask.cpp b/codes/task/src/SimpleTask.cpp index 049fd0cc..ce53ff3c 100644 --- a/codes/task/src/SimpleTask.cpp +++ b/codes/task/src/SimpleTask.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/src/State.cpp b/codes/task/src/State.cpp index 8bed39f3..7435580c 100644 --- a/codes/task/src/State.cpp +++ b/codes/task/src/State.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/src/Task.cpp b/codes/task/src/Task.cpp index 5981c9c7..145abbdb 100644 --- a/codes/task/src/Task.cpp +++ b/codes/task/src/Task.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/src/TaskCom.cpp b/codes/task/src/TaskCom.cpp index 15b6e448..c6cbeaee 100644 --- a/codes/task/src/TaskCom.cpp +++ b/codes/task/src/TaskCom.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/src/TaskImp.cpp b/codes/task/src/TaskImp.cpp index eb1816ca..d943fffd 100644 --- a/codes/task/src/TaskImp.cpp +++ b/codes/task/src/TaskImp.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/src/TaskRegister.cpp b/codes/task/src/TaskRegister.cpp index 4df159ce..4309a123 100644 --- a/codes/task/src/TaskRegister.cpp +++ b/codes/task/src/TaskRegister.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/src/TaskState.cpp b/codes/task/src/TaskState.cpp index 8a082242..e5069c18 100644 --- a/codes/task/src/TaskState.cpp +++ b/codes/task/src/TaskState.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/task/src/WriteTask.cpp b/codes/task/src/WriteTask.cpp index 1eecb26a..f32953e1 100644 --- a/codes/task/src/WriteTask.cpp +++ b/codes/task/src/WriteTask.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/test/include/JsonTest.h b/codes/test/include/JsonTest.h index dd773b41..b6afaa5f 100644 --- a/codes/test/include/JsonTest.h +++ b/codes/test/include/JsonTest.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/test/include/Test.h b/codes/test/include/Test.h index 92e56818..d26bfd88 100644 --- a/codes/test/include/Test.h +++ b/codes/test/include/Test.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/test/src/JsonTest.cpp b/codes/test/src/JsonTest.cpp index 9ff8ac3e..5b6b9d71 100644 --- a/codes/test/src/JsonTest.cpp +++ b/codes/test/src/JsonTest.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/test/src/Test.cpp b/codes/test/src/Test.cpp index b9cbfc2c..9d8814eb 100644 --- a/codes/test/src/Test.cpp +++ b/codes/test/src/Test.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/theory/include/Sod.h b/codes/theory/include/Sod.h index 88c6e3b6..74f4bcc3 100644 --- a/codes/theory/include/Sod.h +++ b/codes/theory/include/Sod.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/theory/include/Theory.h b/codes/theory/include/Theory.h index ae664397..f7f25b22 100644 --- a/codes/theory/include/Theory.h +++ b/codes/theory/include/Theory.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/theory/src/Sod.cpp b/codes/theory/src/Sod.cpp index b806e9e4..dfd0f1e0 100644 --- a/codes/theory/src/Sod.cpp +++ b/codes/theory/src/Sod.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/theory/src/Theory.cpp b/codes/theory/src/Theory.cpp index e7e23d4a..9ae95c04 100644 --- a/codes/theory/src/Theory.cpp +++ b/codes/theory/src/Theory.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/time/include/TimeSpan.h b/codes/time/include/TimeSpan.h index 24174e9a..94995d5e 100644 --- a/codes/time/include/TimeSpan.h +++ b/codes/time/include/TimeSpan.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/time/include/TimeTest.h b/codes/time/include/TimeTest.h index 8bd99c56..6005b141 100644 --- a/codes/time/include/TimeTest.h +++ b/codes/time/include/TimeTest.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/time/src/TimeSpan.cpp b/codes/time/src/TimeSpan.cpp index d62b8ceb..7e4140f0 100644 --- a/codes/time/src/TimeSpan.cpp +++ b/codes/time/src/TimeSpan.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/time/src/TimeTest.cpp b/codes/time/src/TimeTest.cpp index 6d2b8b2e..acb06532 100644 --- a/codes/time/src/TimeTest.cpp +++ b/codes/time/src/TimeTest.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/include/TurbBcSolver.h b/codes/turb/include/TurbBcSolver.h index a582aac1..9fc1753d 100644 --- a/codes/turb/include/TurbBcSolver.h +++ b/codes/turb/include/TurbBcSolver.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/include/TurbCom.h b/codes/turb/include/TurbCom.h index 04b867e1..2f576eca 100644 --- a/codes/turb/include/TurbCom.h +++ b/codes/turb/include/TurbCom.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/include/TurbCtrl.h b/codes/turb/include/TurbCtrl.h index e920811f..4882b0f6 100644 --- a/codes/turb/include/TurbCtrl.h +++ b/codes/turb/include/TurbCtrl.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/include/TurbInvFlux.h b/codes/turb/include/TurbInvFlux.h index 14e437b9..90a34f48 100644 --- a/codes/turb/include/TurbInvFlux.h +++ b/codes/turb/include/TurbInvFlux.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/include/TurbLusgs.h b/codes/turb/include/TurbLusgs.h index 680cc239..f8109a23 100644 --- a/codes/turb/include/TurbLusgs.h +++ b/codes/turb/include/TurbLusgs.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/include/TurbRestart.h b/codes/turb/include/TurbRestart.h index cef6e486..3b624aaa 100644 --- a/codes/turb/include/TurbRestart.h +++ b/codes/turb/include/TurbRestart.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/include/TurbRhs.h b/codes/turb/include/TurbRhs.h index 606dbdd6..6f21bb16 100644 --- a/codes/turb/include/TurbRhs.h +++ b/codes/turb/include/TurbRhs.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/include/TurbSolver.h b/codes/turb/include/TurbSolver.h index 3e883dc4..49d9494a 100644 --- a/codes/turb/include/TurbSolver.h +++ b/codes/turb/include/TurbSolver.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/include/TurbSolverImp.h b/codes/turb/include/TurbSolverImp.h index 763a82dc..65bf47f1 100644 --- a/codes/turb/include/TurbSolverImp.h +++ b/codes/turb/include/TurbSolverImp.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/include/TurbSpectrum.h b/codes/turb/include/TurbSpectrum.h index 097c295f..4206e3b9 100644 --- a/codes/turb/include/TurbSpectrum.h +++ b/codes/turb/include/TurbSpectrum.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/include/TurbSrcFlux.h b/codes/turb/include/TurbSrcFlux.h index 00b151d9..c57dff51 100644 --- a/codes/turb/include/TurbSrcFlux.h +++ b/codes/turb/include/TurbSrcFlux.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/include/TurbTrans.h b/codes/turb/include/TurbTrans.h index 9eae03dc..02409c26 100644 --- a/codes/turb/include/TurbTrans.h +++ b/codes/turb/include/TurbTrans.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/include/TurbUnsteady.h b/codes/turb/include/TurbUnsteady.h index fa57238b..95893998 100644 --- a/codes/turb/include/TurbUnsteady.h +++ b/codes/turb/include/TurbUnsteady.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/include/TurbUpdate.h b/codes/turb/include/TurbUpdate.h index b4ee7c33..c2b29ba5 100644 --- a/codes/turb/include/TurbUpdate.h +++ b/codes/turb/include/TurbUpdate.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/include/TurbVisFlux.h b/codes/turb/include/TurbVisFlux.h index 83f29180..2c054806 100644 --- a/codes/turb/include/TurbVisFlux.h +++ b/codes/turb/include/TurbVisFlux.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/src/TurbBcSolver.cpp b/codes/turb/src/TurbBcSolver.cpp index 8f5bac59..5fe18f69 100644 --- a/codes/turb/src/TurbBcSolver.cpp +++ b/codes/turb/src/TurbBcSolver.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/src/TurbCom.cpp b/codes/turb/src/TurbCom.cpp index 951e0ee2..0f055f4d 100644 --- a/codes/turb/src/TurbCom.cpp +++ b/codes/turb/src/TurbCom.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/src/TurbCtrl.cpp b/codes/turb/src/TurbCtrl.cpp index 105ac93f..f08f94c3 100644 --- a/codes/turb/src/TurbCtrl.cpp +++ b/codes/turb/src/TurbCtrl.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/src/TurbInvFlux.cpp b/codes/turb/src/TurbInvFlux.cpp index 8eb36686..194f9375 100644 --- a/codes/turb/src/TurbInvFlux.cpp +++ b/codes/turb/src/TurbInvFlux.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/src/TurbLusgs.cpp b/codes/turb/src/TurbLusgs.cpp index 29d9668b..3dcda5e0 100644 --- a/codes/turb/src/TurbLusgs.cpp +++ b/codes/turb/src/TurbLusgs.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/src/TurbRestart.cpp b/codes/turb/src/TurbRestart.cpp index cc877718..569939ca 100644 --- a/codes/turb/src/TurbRestart.cpp +++ b/codes/turb/src/TurbRestart.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/src/TurbRhs.cpp b/codes/turb/src/TurbRhs.cpp index 60974424..18c12dfe 100644 --- a/codes/turb/src/TurbRhs.cpp +++ b/codes/turb/src/TurbRhs.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/src/TurbSolver.cpp b/codes/turb/src/TurbSolver.cpp index 75412357..5a246e3e 100644 --- a/codes/turb/src/TurbSolver.cpp +++ b/codes/turb/src/TurbSolver.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/src/TurbSolverImp.cpp b/codes/turb/src/TurbSolverImp.cpp index 6c795ce2..3002f730 100644 --- a/codes/turb/src/TurbSolverImp.cpp +++ b/codes/turb/src/TurbSolverImp.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/src/TurbSpectrum.cpp b/codes/turb/src/TurbSpectrum.cpp index 0eaf01da..b9512825 100644 --- a/codes/turb/src/TurbSpectrum.cpp +++ b/codes/turb/src/TurbSpectrum.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/src/TurbSrcFlux.cpp b/codes/turb/src/TurbSrcFlux.cpp index d66ba359..f52a651f 100644 --- a/codes/turb/src/TurbSrcFlux.cpp +++ b/codes/turb/src/TurbSrcFlux.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/src/TurbTrans.cpp b/codes/turb/src/TurbTrans.cpp index 8d8eceda..48cfae12 100644 --- a/codes/turb/src/TurbTrans.cpp +++ b/codes/turb/src/TurbTrans.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/src/TurbUnsteady.cpp b/codes/turb/src/TurbUnsteady.cpp index 64863a8b..ecc2863e 100644 --- a/codes/turb/src/TurbUnsteady.cpp +++ b/codes/turb/src/TurbUnsteady.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/src/TurbUpdate.cpp b/codes/turb/src/TurbUpdate.cpp index ff9b78b3..be698268 100644 --- a/codes/turb/src/TurbUpdate.cpp +++ b/codes/turb/src/TurbUpdate.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/turb/src/TurbVisFlux.cpp b/codes/turb/src/TurbVisFlux.cpp index 6ddc2ead..fb2ed734 100644 --- a/codes/turb/src/TurbVisFlux.cpp +++ b/codes/turb/src/TurbVisFlux.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uins/include/UINsBcSolver.h b/codes/uins/include/UINsBcSolver.h index 38f8637e..b7a0b08b 100644 --- a/codes/uins/include/UINsBcSolver.h +++ b/codes/uins/include/UINsBcSolver.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uins/include/UINsCom.h b/codes/uins/include/UINsCom.h index 2be66660..74465747 100644 --- a/codes/uins/include/UINsCom.h +++ b/codes/uins/include/UINsCom.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uins/include/UINsGrad.h b/codes/uins/include/UINsGrad.h index b0e0417e..aed8ef74 100644 --- a/codes/uins/include/UINsGrad.h +++ b/codes/uins/include/UINsGrad.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uins/include/UINsInvterm.h b/codes/uins/include/UINsInvterm.h index 3dcc4e02..c8d88f20 100644 --- a/codes/uins/include/UINsInvterm.h +++ b/codes/uins/include/UINsInvterm.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uins/include/UINsLimiter.h b/codes/uins/include/UINsLimiter.h index f3c14124..1519d220 100644 --- a/codes/uins/include/UINsLimiter.h +++ b/codes/uins/include/UINsLimiter.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uins/include/UINsLusgs.h b/codes/uins/include/UINsLusgs.h index f2dc330b..50b157fa 100644 --- a/codes/uins/include/UINsLusgs.h +++ b/codes/uins/include/UINsLusgs.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uins/include/UINsRestart.h b/codes/uins/include/UINsRestart.h index 0841f318..75b83020 100644 --- a/codes/uins/include/UINsRestart.h +++ b/codes/uins/include/UINsRestart.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uins/include/UINsSolver.h b/codes/uins/include/UINsSolver.h index d418d811..d672cca4 100644 --- a/codes/uins/include/UINsSolver.h +++ b/codes/uins/include/UINsSolver.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uins/include/UINsSpectrum.h b/codes/uins/include/UINsSpectrum.h index b344b264..2ddfdcc9 100644 --- a/codes/uins/include/UINsSpectrum.h +++ b/codes/uins/include/UINsSpectrum.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uins/include/UINsUnsteady.h b/codes/uins/include/UINsUnsteady.h index 3f18a73b..b34ab19c 100644 --- a/codes/uins/include/UINsUnsteady.h +++ b/codes/uins/include/UINsUnsteady.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uins/include/UINsUpdate.h b/codes/uins/include/UINsUpdate.h index d0dd5967..cd370583 100644 --- a/codes/uins/include/UINsUpdate.h +++ b/codes/uins/include/UINsUpdate.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uins/include/UINsVisterm.h b/codes/uins/include/UINsVisterm.h index 379ad416..baa02f16 100644 --- a/codes/uins/include/UINsVisterm.h +++ b/codes/uins/include/UINsVisterm.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uins/src/UINsBcSolver.cpp b/codes/uins/src/UINsBcSolver.cpp index 3a32f636..23a083f2 100644 --- a/codes/uins/src/UINsBcSolver.cpp +++ b/codes/uins/src/UINsBcSolver.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uins/src/UINsCom.cpp b/codes/uins/src/UINsCom.cpp index 36be6509..ce8d9f0a 100644 --- a/codes/uins/src/UINsCom.cpp +++ b/codes/uins/src/UINsCom.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uins/src/UINsGrad.cpp b/codes/uins/src/UINsGrad.cpp index 0d79c532..a916f3a4 100644 --- a/codes/uins/src/UINsGrad.cpp +++ b/codes/uins/src/UINsGrad.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uins/src/UINsInvterm.cpp b/codes/uins/src/UINsInvterm.cpp index 9ba1ad0b..e6815d7d 100644 --- a/codes/uins/src/UINsInvterm.cpp +++ b/codes/uins/src/UINsInvterm.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uins/src/UINsLimiter.cpp b/codes/uins/src/UINsLimiter.cpp index 91623e2e..c74cb53a 100644 --- a/codes/uins/src/UINsLimiter.cpp +++ b/codes/uins/src/UINsLimiter.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uins/src/UINsLusgs.cpp b/codes/uins/src/UINsLusgs.cpp index 2dd9e80d..d130e294 100644 --- a/codes/uins/src/UINsLusgs.cpp +++ b/codes/uins/src/UINsLusgs.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uins/src/UINsRestart.cpp b/codes/uins/src/UINsRestart.cpp index 6b056f8e..5a29459a 100644 --- a/codes/uins/src/UINsRestart.cpp +++ b/codes/uins/src/UINsRestart.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uins/src/UINsSolver.cpp b/codes/uins/src/UINsSolver.cpp index fe31de51..890ce347 100644 --- a/codes/uins/src/UINsSolver.cpp +++ b/codes/uins/src/UINsSolver.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uins/src/UINsSpectrum.cpp b/codes/uins/src/UINsSpectrum.cpp index 6aad4702..beede03f 100644 --- a/codes/uins/src/UINsSpectrum.cpp +++ b/codes/uins/src/UINsSpectrum.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uins/src/UINsUnsteady.cpp b/codes/uins/src/UINsUnsteady.cpp index e6211e71..e25ba614 100644 --- a/codes/uins/src/UINsUnsteady.cpp +++ b/codes/uins/src/UINsUnsteady.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uins/src/UINsUpdate.cpp b/codes/uins/src/UINsUpdate.cpp index f281ce9d..e8540d0b 100644 --- a/codes/uins/src/UINsUpdate.cpp +++ b/codes/uins/src/UINsUpdate.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uins/src/UINsVisterm.cpp b/codes/uins/src/UINsVisterm.cpp index e0a61410..589eeb56 100644 --- a/codes/uins/src/UINsVisterm.cpp +++ b/codes/uins/src/UINsVisterm.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/include/UNsBcSolver.h b/codes/uns/include/UNsBcSolver.h index f3ea85fb..a8d78b0c 100644 --- a/codes/uns/include/UNsBcSolver.h +++ b/codes/uns/include/UNsBcSolver.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/include/UNsCom.h b/codes/uns/include/UNsCom.h index 39bc3362..f731bc6e 100644 --- a/codes/uns/include/UNsCom.h +++ b/codes/uns/include/UNsCom.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/include/UNsGrad.h b/codes/uns/include/UNsGrad.h index 09658439..173bcd42 100644 --- a/codes/uns/include/UNsGrad.h +++ b/codes/uns/include/UNsGrad.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/include/UNsInvFlux.h b/codes/uns/include/UNsInvFlux.h index bd8036c3..70a57998 100644 --- a/codes/uns/include/UNsInvFlux.h +++ b/codes/uns/include/UNsInvFlux.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/include/UNsLimiter.h b/codes/uns/include/UNsLimiter.h index 4050f42f..3e6dc262 100644 --- a/codes/uns/include/UNsLimiter.h +++ b/codes/uns/include/UNsLimiter.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/include/UNsLusgs.h b/codes/uns/include/UNsLusgs.h index 86863a10..93e5b0da 100644 --- a/codes/uns/include/UNsLusgs.h +++ b/codes/uns/include/UNsLusgs.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/include/UNsRestart.h b/codes/uns/include/UNsRestart.h index 0f12ce0f..4e2d35bc 100644 --- a/codes/uns/include/UNsRestart.h +++ b/codes/uns/include/UNsRestart.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/include/UNsSolver.h b/codes/uns/include/UNsSolver.h index a43123b1..0636f2be 100644 --- a/codes/uns/include/UNsSolver.h +++ b/codes/uns/include/UNsSolver.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/include/UNsSpectrum.h b/codes/uns/include/UNsSpectrum.h index 27812c82..8dfd7bfa 100644 --- a/codes/uns/include/UNsSpectrum.h +++ b/codes/uns/include/UNsSpectrum.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/include/UNsUnsteady.h b/codes/uns/include/UNsUnsteady.h index 9c580e63..c636f20a 100644 --- a/codes/uns/include/UNsUnsteady.h +++ b/codes/uns/include/UNsUnsteady.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/include/UNsUpdate.h b/codes/uns/include/UNsUpdate.h index a2213766..d4bb7e4e 100644 --- a/codes/uns/include/UNsUpdate.h +++ b/codes/uns/include/UNsUpdate.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/include/UNsVisFlux.h b/codes/uns/include/UNsVisFlux.h index bcc3c8b4..cc7b5360 100644 --- a/codes/uns/include/UNsVisFlux.h +++ b/codes/uns/include/UNsVisFlux.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/include/UTimeStep.h b/codes/uns/include/UTimeStep.h index a37ac716..b5c95804 100644 --- a/codes/uns/include/UTimeStep.h +++ b/codes/uns/include/UTimeStep.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/src/UNsBcSolver.cpp b/codes/uns/src/UNsBcSolver.cpp index 140a2840..a899ac92 100644 --- a/codes/uns/src/UNsBcSolver.cpp +++ b/codes/uns/src/UNsBcSolver.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/src/UNsCom.cpp b/codes/uns/src/UNsCom.cpp index f06f0f6b..8cf454d1 100644 --- a/codes/uns/src/UNsCom.cpp +++ b/codes/uns/src/UNsCom.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/src/UNsGrad.cpp b/codes/uns/src/UNsGrad.cpp index 9ca2711b..59ec1e45 100644 --- a/codes/uns/src/UNsGrad.cpp +++ b/codes/uns/src/UNsGrad.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/src/UNsInvFlux.cpp b/codes/uns/src/UNsInvFlux.cpp index 69fff695..2b495617 100644 --- a/codes/uns/src/UNsInvFlux.cpp +++ b/codes/uns/src/UNsInvFlux.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/src/UNsLimiter.cpp b/codes/uns/src/UNsLimiter.cpp index 8be80768..18e44e3c 100644 --- a/codes/uns/src/UNsLimiter.cpp +++ b/codes/uns/src/UNsLimiter.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/src/UNsLusgs.cpp b/codes/uns/src/UNsLusgs.cpp index fefbbba2..f3723cee 100644 --- a/codes/uns/src/UNsLusgs.cpp +++ b/codes/uns/src/UNsLusgs.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/src/UNsRestart.cpp b/codes/uns/src/UNsRestart.cpp index 38194a28..44776f44 100644 --- a/codes/uns/src/UNsRestart.cpp +++ b/codes/uns/src/UNsRestart.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/src/UNsSolver.cpp b/codes/uns/src/UNsSolver.cpp index 93f27391..1f9e38d3 100644 --- a/codes/uns/src/UNsSolver.cpp +++ b/codes/uns/src/UNsSolver.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/src/UNsSpectrum.cpp b/codes/uns/src/UNsSpectrum.cpp index 705ff7bb..be029621 100644 --- a/codes/uns/src/UNsSpectrum.cpp +++ b/codes/uns/src/UNsSpectrum.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/src/UNsUnsteady.cpp b/codes/uns/src/UNsUnsteady.cpp index 3cc3bf1d..d0393901 100644 --- a/codes/uns/src/UNsUnsteady.cpp +++ b/codes/uns/src/UNsUnsteady.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/src/UNsUpdate.cpp b/codes/uns/src/UNsUpdate.cpp index 239cc1b9..6162608a 100644 --- a/codes/uns/src/UNsUpdate.cpp +++ b/codes/uns/src/UNsUpdate.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/src/UNsVisFlux.cpp b/codes/uns/src/UNsVisFlux.cpp index fe809c40..2f7b7aeb 100644 --- a/codes/uns/src/UNsVisFlux.cpp +++ b/codes/uns/src/UNsVisFlux.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uns/src/UTimeStep.cpp b/codes/uns/src/UTimeStep.cpp index 16a66615..2cfaa588 100644 --- a/codes/uns/src/UTimeStep.cpp +++ b/codes/uns/src/UTimeStep.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/unsteady/include/Unsteady.h b/codes/unsteady/include/Unsteady.h index 8d4d0458..f393ee37 100644 --- a/codes/unsteady/include/Unsteady.h +++ b/codes/unsteady/include/Unsteady.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/unsteady/include/UnsteadyImp.h b/codes/unsteady/include/UnsteadyImp.h index 93db079f..2fce3c8d 100644 --- a/codes/unsteady/include/UnsteadyImp.h +++ b/codes/unsteady/include/UnsteadyImp.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/unsteady/include/UnsteadyTaskReg.h b/codes/unsteady/include/UnsteadyTaskReg.h index 9166fe4f..940c53a6 100644 --- a/codes/unsteady/include/UnsteadyTaskReg.h +++ b/codes/unsteady/include/UnsteadyTaskReg.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/unsteady/include/UsdBasic.h b/codes/unsteady/include/UsdBasic.h index c2758d84..2a339058 100644 --- a/codes/unsteady/include/UsdBasic.h +++ b/codes/unsteady/include/UsdBasic.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/unsteady/include/UsdData.h b/codes/unsteady/include/UsdData.h index a1601f91..01ca1908 100644 --- a/codes/unsteady/include/UsdData.h +++ b/codes/unsteady/include/UsdData.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/unsteady/include/UsdField.h b/codes/unsteady/include/UsdField.h index e57a2903..99dd00f0 100644 --- a/codes/unsteady/include/UsdField.h +++ b/codes/unsteady/include/UsdField.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/unsteady/include/UsdPara.h b/codes/unsteady/include/UsdPara.h index 366c236b..34a7afe1 100644 --- a/codes/unsteady/include/UsdPara.h +++ b/codes/unsteady/include/UsdPara.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/unsteady/src/Unsteady.cpp b/codes/unsteady/src/Unsteady.cpp index 3f12a501..c3b59e0b 100644 --- a/codes/unsteady/src/Unsteady.cpp +++ b/codes/unsteady/src/Unsteady.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/unsteady/src/UnsteadyImp.cpp b/codes/unsteady/src/UnsteadyImp.cpp index e9949189..cd10bb80 100644 --- a/codes/unsteady/src/UnsteadyImp.cpp +++ b/codes/unsteady/src/UnsteadyImp.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/unsteady/src/UnsteadyTaskReg.cpp b/codes/unsteady/src/UnsteadyTaskReg.cpp index 065c4b3d..f590d69d 100644 --- a/codes/unsteady/src/UnsteadyTaskReg.cpp +++ b/codes/unsteady/src/UnsteadyTaskReg.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/unsteady/src/UsdBasic.cpp b/codes/unsteady/src/UsdBasic.cpp index 25d4e51a..3ff88684 100644 --- a/codes/unsteady/src/UsdBasic.cpp +++ b/codes/unsteady/src/UsdBasic.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/unsteady/src/UsdData.cpp b/codes/unsteady/src/UsdData.cpp index 9d772d3f..69fae2d4 100644 --- a/codes/unsteady/src/UsdData.cpp +++ b/codes/unsteady/src/UsdData.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/unsteady/src/UsdField.cpp b/codes/unsteady/src/UsdField.cpp index 823e8fc7..bfbbd4a3 100644 --- a/codes/unsteady/src/UsdField.cpp +++ b/codes/unsteady/src/UsdField.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/unsteady/src/UsdPara.cpp b/codes/unsteady/src/UsdPara.cpp index 62326be3..9ee5c830 100644 --- a/codes/unsteady/src/UsdPara.cpp +++ b/codes/unsteady/src/UsdPara.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/update/include/Update.h b/codes/update/include/Update.h index 751f889d..7611ec91 100644 --- a/codes/update/include/Update.h +++ b/codes/update/include/Update.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/update/include/UpdateTaskReg.h b/codes/update/include/UpdateTaskReg.h index 584cd682..4354e6bf 100644 --- a/codes/update/include/UpdateTaskReg.h +++ b/codes/update/include/UpdateTaskReg.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/update/src/Update.cpp b/codes/update/src/Update.cpp index 1111b2bd..f33c48ee 100644 --- a/codes/update/src/Update.cpp +++ b/codes/update/src/Update.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/update/src/UpdateTaskReg.cpp b/codes/update/src/UpdateTaskReg.cpp index 9613e9e9..45426274 100644 --- a/codes/update/src/UpdateTaskReg.cpp +++ b/codes/update/src/UpdateTaskReg.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/usolver/include/UBcSolver.h b/codes/usolver/include/UBcSolver.h index 512edf36..0ca27a20 100644 --- a/codes/usolver/include/UBcSolver.h +++ b/codes/usolver/include/UBcSolver.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/usolver/include/UCom.h b/codes/usolver/include/UCom.h index 4b36666f..8fc28f5e 100644 --- a/codes/usolver/include/UCom.h +++ b/codes/usolver/include/UCom.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/usolver/include/UGrad.h b/codes/usolver/include/UGrad.h index 7e7772e6..e5c6dab7 100644 --- a/codes/usolver/include/UGrad.h +++ b/codes/usolver/include/UGrad.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/usolver/include/ULhs.h b/codes/usolver/include/ULhs.h index 969055c4..15c146e8 100644 --- a/codes/usolver/include/ULhs.h +++ b/codes/usolver/include/ULhs.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/usolver/include/ULimiter.h b/codes/usolver/include/ULimiter.h index 60300bd1..4aa1c2bc 100644 --- a/codes/usolver/include/ULimiter.h +++ b/codes/usolver/include/ULimiter.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/usolver/include/UResidual.h b/codes/usolver/include/UResidual.h index ff6012f5..d56c85df 100644 --- a/codes/usolver/include/UResidual.h +++ b/codes/usolver/include/UResidual.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/usolver/include/UUnsteady.h b/codes/usolver/include/UUnsteady.h index 7ca4db91..f2f2cdb8 100644 --- a/codes/usolver/include/UUnsteady.h +++ b/codes/usolver/include/UUnsteady.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/usolver/include/UVisualize.h b/codes/usolver/include/UVisualize.h index a74cc19b..31ff18a6 100644 --- a/codes/usolver/include/UVisualize.h +++ b/codes/usolver/include/UVisualize.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/usolver/include/VisGrad.h b/codes/usolver/include/VisGrad.h index 79b72f84..dab2baef 100644 --- a/codes/usolver/include/VisGrad.h +++ b/codes/usolver/include/VisGrad.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/usolver/src/UBcSolver.cpp b/codes/usolver/src/UBcSolver.cpp index 2f852753..104883b9 100644 --- a/codes/usolver/src/UBcSolver.cpp +++ b/codes/usolver/src/UBcSolver.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/usolver/src/UCom.cpp b/codes/usolver/src/UCom.cpp index a271644c..abd2b576 100644 --- a/codes/usolver/src/UCom.cpp +++ b/codes/usolver/src/UCom.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/usolver/src/UGrad.cpp b/codes/usolver/src/UGrad.cpp index da9596f2..613e058e 100644 --- a/codes/usolver/src/UGrad.cpp +++ b/codes/usolver/src/UGrad.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/usolver/src/ULhs.cpp b/codes/usolver/src/ULhs.cpp index 8cd972e3..579e4d59 100644 --- a/codes/usolver/src/ULhs.cpp +++ b/codes/usolver/src/ULhs.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/usolver/src/ULimiter.cpp b/codes/usolver/src/ULimiter.cpp index 1a52b8d9..b80d3758 100644 --- a/codes/usolver/src/ULimiter.cpp +++ b/codes/usolver/src/ULimiter.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/usolver/src/UResidual.cpp b/codes/usolver/src/UResidual.cpp index aa81506b..aa766f0a 100644 --- a/codes/usolver/src/UResidual.cpp +++ b/codes/usolver/src/UResidual.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/usolver/src/UUnsteady.cpp b/codes/usolver/src/UUnsteady.cpp index 594cb4cf..0cfebe02 100644 --- a/codes/usolver/src/UUnsteady.cpp +++ b/codes/usolver/src/UUnsteady.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/usolver/src/UVisualize.cpp b/codes/usolver/src/UVisualize.cpp index 36dae893..eff7075b 100644 --- a/codes/usolver/src/UVisualize.cpp +++ b/codes/usolver/src/UVisualize.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/usolver/src/VisGrad.cpp b/codes/usolver/src/VisGrad.cpp index 0c907e74..f65e5bd6 100644 --- a/codes/usolver/src/VisGrad.cpp +++ b/codes/usolver/src/VisGrad.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/include/UTurbBcSolver.h b/codes/uturb/include/UTurbBcSolver.h index a225431e..b40a4a0d 100644 --- a/codes/uturb/include/UTurbBcSolver.h +++ b/codes/uturb/include/UTurbBcSolver.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/include/UTurbCom.h b/codes/uturb/include/UTurbCom.h index 2e94bcfe..b9121c17 100644 --- a/codes/uturb/include/UTurbCom.h +++ b/codes/uturb/include/UTurbCom.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/include/UTurbGrad.h b/codes/uturb/include/UTurbGrad.h index adc4dd68..6462ba17 100644 --- a/codes/uturb/include/UTurbGrad.h +++ b/codes/uturb/include/UTurbGrad.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/include/UTurbInvFlux.h b/codes/uturb/include/UTurbInvFlux.h index c8ce2ce8..76118252 100644 --- a/codes/uturb/include/UTurbInvFlux.h +++ b/codes/uturb/include/UTurbInvFlux.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/include/UTurbLimiter.h b/codes/uturb/include/UTurbLimiter.h index 4b946cb4..a63edf7d 100644 --- a/codes/uturb/include/UTurbLimiter.h +++ b/codes/uturb/include/UTurbLimiter.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/include/UTurbLusgs.h b/codes/uturb/include/UTurbLusgs.h index b7937785..c0bf333b 100644 --- a/codes/uturb/include/UTurbLusgs.h +++ b/codes/uturb/include/UTurbLusgs.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/include/UTurbRestart.h b/codes/uturb/include/UTurbRestart.h index a5be3f93..60c7587b 100644 --- a/codes/uturb/include/UTurbRestart.h +++ b/codes/uturb/include/UTurbRestart.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/include/UTurbSolver.h b/codes/uturb/include/UTurbSolver.h index b6ebc330..a0ad6b64 100644 --- a/codes/uturb/include/UTurbSolver.h +++ b/codes/uturb/include/UTurbSolver.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/include/UTurbSpectrum.h b/codes/uturb/include/UTurbSpectrum.h index f2a9cbbe..438b31b2 100644 --- a/codes/uturb/include/UTurbSpectrum.h +++ b/codes/uturb/include/UTurbSpectrum.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/include/UTurbSrcFlux.h b/codes/uturb/include/UTurbSrcFlux.h index 031b1b86..5b9bee59 100644 --- a/codes/uturb/include/UTurbSrcFlux.h +++ b/codes/uturb/include/UTurbSrcFlux.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/include/UTurbUnsteady.h b/codes/uturb/include/UTurbUnsteady.h index ed29d4a7..c7248bd0 100644 --- a/codes/uturb/include/UTurbUnsteady.h +++ b/codes/uturb/include/UTurbUnsteady.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/include/UTurbUpdate.h b/codes/uturb/include/UTurbUpdate.h index 9d90d4c3..303ad033 100644 --- a/codes/uturb/include/UTurbUpdate.h +++ b/codes/uturb/include/UTurbUpdate.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/include/UTurbVisFlux.h b/codes/uturb/include/UTurbVisFlux.h index 574f30d2..402942de 100644 --- a/codes/uturb/include/UTurbVisFlux.h +++ b/codes/uturb/include/UTurbVisFlux.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/src/UTurbBcSolver.cpp b/codes/uturb/src/UTurbBcSolver.cpp index 373be97e..ad46d1e8 100644 --- a/codes/uturb/src/UTurbBcSolver.cpp +++ b/codes/uturb/src/UTurbBcSolver.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/src/UTurbCom.cpp b/codes/uturb/src/UTurbCom.cpp index 534a7a33..4c4756b0 100644 --- a/codes/uturb/src/UTurbCom.cpp +++ b/codes/uturb/src/UTurbCom.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/src/UTurbGrad.cpp b/codes/uturb/src/UTurbGrad.cpp index dac38ce8..6651e74d 100644 --- a/codes/uturb/src/UTurbGrad.cpp +++ b/codes/uturb/src/UTurbGrad.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/src/UTurbInvFlux.cpp b/codes/uturb/src/UTurbInvFlux.cpp index 43f5c816..31cf6d4e 100644 --- a/codes/uturb/src/UTurbInvFlux.cpp +++ b/codes/uturb/src/UTurbInvFlux.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/src/UTurbLimiter.cpp b/codes/uturb/src/UTurbLimiter.cpp index 9e5d7bf6..b43d8bb8 100644 --- a/codes/uturb/src/UTurbLimiter.cpp +++ b/codes/uturb/src/UTurbLimiter.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/src/UTurbLusgs.cpp b/codes/uturb/src/UTurbLusgs.cpp index b21a618c..a7b022ad 100644 --- a/codes/uturb/src/UTurbLusgs.cpp +++ b/codes/uturb/src/UTurbLusgs.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/src/UTurbRestart.cpp b/codes/uturb/src/UTurbRestart.cpp index 4f0f3b46..cdb704db 100644 --- a/codes/uturb/src/UTurbRestart.cpp +++ b/codes/uturb/src/UTurbRestart.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/src/UTurbSolver.cpp b/codes/uturb/src/UTurbSolver.cpp index faed0db5..1368795a 100644 --- a/codes/uturb/src/UTurbSolver.cpp +++ b/codes/uturb/src/UTurbSolver.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/src/UTurbSpectrum.cpp b/codes/uturb/src/UTurbSpectrum.cpp index 2dc7a4eb..9157c0d1 100644 --- a/codes/uturb/src/UTurbSpectrum.cpp +++ b/codes/uturb/src/UTurbSpectrum.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/src/UTurbSrcFlux.cpp b/codes/uturb/src/UTurbSrcFlux.cpp index 73596fa3..119d9464 100644 --- a/codes/uturb/src/UTurbSrcFlux.cpp +++ b/codes/uturb/src/UTurbSrcFlux.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/src/UTurbUnsteady.cpp b/codes/uturb/src/UTurbUnsteady.cpp index 9d5c430f..4142f775 100644 --- a/codes/uturb/src/UTurbUnsteady.cpp +++ b/codes/uturb/src/UTurbUnsteady.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/src/UTurbUpdate.cpp b/codes/uturb/src/UTurbUpdate.cpp index 7208b8a7..a8f6e5ac 100644 --- a/codes/uturb/src/UTurbUpdate.cpp +++ b/codes/uturb/src/UTurbUpdate.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/uturb/src/UTurbVisFlux.cpp b/codes/uturb/src/UTurbVisFlux.cpp index 2114da29..9d219e81 100644 --- a/codes/uturb/src/UTurbVisFlux.cpp +++ b/codes/uturb/src/UTurbVisFlux.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/visual/include/FaceJoint.h b/codes/visual/include/FaceJoint.h index ee817d90..c97b13ae 100644 --- a/codes/visual/include/FaceJoint.h +++ b/codes/visual/include/FaceJoint.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/visual/include/HeatFlux.h b/codes/visual/include/HeatFlux.h index eb2e623c..34a4c4ff 100644 --- a/codes/visual/include/HeatFlux.h +++ b/codes/visual/include/HeatFlux.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/visual/include/HeatFluxTask.h b/codes/visual/include/HeatFluxTask.h index dcbc2afb..2ceed9e7 100644 --- a/codes/visual/include/HeatFluxTask.h +++ b/codes/visual/include/HeatFluxTask.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/visual/include/HeatFluxTaskReg.h b/codes/visual/include/HeatFluxTaskReg.h index bb51cb0a..34826b1d 100644 --- a/codes/visual/include/HeatFluxTaskReg.h +++ b/codes/visual/include/HeatFluxTaskReg.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/visual/include/LaminarPlate.h b/codes/visual/include/LaminarPlate.h index 567d8e96..1a24fdd0 100644 --- a/codes/visual/include/LaminarPlate.h +++ b/codes/visual/include/LaminarPlate.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/visual/include/NodeField.h b/codes/visual/include/NodeField.h index 3bc54f56..de48acf9 100644 --- a/codes/visual/include/NodeField.h +++ b/codes/visual/include/NodeField.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/visual/include/Plate.h b/codes/visual/include/Plate.h index ae84df86..5befe950 100644 --- a/codes/visual/include/Plate.h +++ b/codes/visual/include/Plate.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/visual/include/TurbPlate.h b/codes/visual/include/TurbPlate.h index b08bb4ae..580547e3 100644 --- a/codes/visual/include/TurbPlate.h +++ b/codes/visual/include/TurbPlate.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/visual/include/VisualTaskReg.h b/codes/visual/include/VisualTaskReg.h index 2c7b0b38..b4d72b4a 100644 --- a/codes/visual/include/VisualTaskReg.h +++ b/codes/visual/include/VisualTaskReg.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/visual/include/Visualize.h b/codes/visual/include/Visualize.h index 06fbb72d..8cdbc9f4 100644 --- a/codes/visual/include/Visualize.h +++ b/codes/visual/include/Visualize.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/visual/include/WallVisual.h b/codes/visual/include/WallVisual.h index 12989cbf..baf90bed 100644 --- a/codes/visual/include/WallVisual.h +++ b/codes/visual/include/WallVisual.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/visual/src/FaceJoint.cpp b/codes/visual/src/FaceJoint.cpp index d5af8dc7..48cbbdd2 100644 --- a/codes/visual/src/FaceJoint.cpp +++ b/codes/visual/src/FaceJoint.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/visual/src/HeatFlux.cpp b/codes/visual/src/HeatFlux.cpp index c9dea237..4afb9013 100644 --- a/codes/visual/src/HeatFlux.cpp +++ b/codes/visual/src/HeatFlux.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/visual/src/HeatFluxTask.cpp b/codes/visual/src/HeatFluxTask.cpp index e19c8295..0c146d4f 100644 --- a/codes/visual/src/HeatFluxTask.cpp +++ b/codes/visual/src/HeatFluxTask.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/visual/src/HeatFluxTaskReg.cpp b/codes/visual/src/HeatFluxTaskReg.cpp index 08a2b4a9..d8a52ad5 100644 --- a/codes/visual/src/HeatFluxTaskReg.cpp +++ b/codes/visual/src/HeatFluxTaskReg.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/visual/src/LaminarPlate.cpp b/codes/visual/src/LaminarPlate.cpp index aea132e1..2c1707b8 100644 --- a/codes/visual/src/LaminarPlate.cpp +++ b/codes/visual/src/LaminarPlate.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/visual/src/NodeField.cpp b/codes/visual/src/NodeField.cpp index 60192c1c..045ae25a 100644 --- a/codes/visual/src/NodeField.cpp +++ b/codes/visual/src/NodeField.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/visual/src/Plate.cpp b/codes/visual/src/Plate.cpp index dca1dc6f..e9824ea0 100644 --- a/codes/visual/src/Plate.cpp +++ b/codes/visual/src/Plate.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/visual/src/TurbPlate.cpp b/codes/visual/src/TurbPlate.cpp index d2dd541a..44d0d367 100644 --- a/codes/visual/src/TurbPlate.cpp +++ b/codes/visual/src/TurbPlate.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/visual/src/VisualTaskReg.cpp b/codes/visual/src/VisualTaskReg.cpp index ad967dab..137e6e60 100644 --- a/codes/visual/src/VisualTaskReg.cpp +++ b/codes/visual/src/VisualTaskReg.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/visual/src/Visualize.cpp b/codes/visual/src/Visualize.cpp index 0e78c44c..16ef2626 100644 --- a/codes/visual/src/Visualize.cpp +++ b/codes/visual/src/Visualize.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/visual/src/WallVisual.cpp b/codes/visual/src/WallVisual.cpp index 36c9d728..c0ef665e 100644 --- a/codes/visual/src/WallVisual.cpp +++ b/codes/visual/src/WallVisual.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/zone/include/GridGroup.h b/codes/zone/include/GridGroup.h index d77ad080..5c5b1b9c 100644 --- a/codes/zone/include/GridGroup.h +++ b/codes/zone/include/GridGroup.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/zone/include/MultiBlock.h b/codes/zone/include/MultiBlock.h index c47fdf4d..43af86cd 100644 --- a/codes/zone/include/MultiBlock.h +++ b/codes/zone/include/MultiBlock.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/zone/include/Zone.h b/codes/zone/include/Zone.h index 4afe7349..1d8ba96e 100644 --- a/codes/zone/include/Zone.h +++ b/codes/zone/include/Zone.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/zone/include/ZoneState.h b/codes/zone/include/ZoneState.h index 98d484ec..dee99a31 100644 --- a/codes/zone/include/ZoneState.h +++ b/codes/zone/include/ZoneState.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/zone/src/GridGroup.cpp b/codes/zone/src/GridGroup.cpp index 6c6a7428..fc78376b 100644 --- a/codes/zone/src/GridGroup.cpp +++ b/codes/zone/src/GridGroup.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/zone/src/MultiBlock.cpp b/codes/zone/src/MultiBlock.cpp index 20868fdf..7bdc3456 100644 --- a/codes/zone/src/MultiBlock.cpp +++ b/codes/zone/src/MultiBlock.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/zone/src/Zone.cpp b/codes/zone/src/Zone.cpp index 355ac9a1..3e4af195 100644 --- a/codes/zone/src/Zone.cpp +++ b/codes/zone/src/Zone.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/codes/zone/src/ZoneState.cpp b/codes/zone/src/ZoneState.cpp index 3f5a94ac..d9b3e7f7 100644 --- a/codes/zone/src/ZoneState.cpp +++ b/codes/zone/src/ZoneState.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/example/1d-linear-convection/weno3/fortran/FortranLibTest/01/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/FortranLibTest/01/CMakeLists.txt new file mode 100644 index 00000000..a31a8fae --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/FortranLibTest/01/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.18) +project(FortranLibTest VERSION 1.0 LANGUAGES Fortran) + +# 设置Fortran标准 +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +set(CMAKE_Fortran_MODULE_DIRECTORY "${CMAKE_BINARY_DIR}/modules") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +# 添加库目录和测试目录 +add_subdirectory(src) +add_subdirectory(tests) diff --git a/example/1d-linear-convection/weno3/fortran/FortranLibTest/01/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/FortranLibTest/01/src/CMakeLists.txt new file mode 100644 index 00000000..29a6e9fe --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/FortranLibTest/01/src/CMakeLists.txt @@ -0,0 +1,2 @@ +# 构建静态库(核心:生成mymath.lib(Windows)/libmymath.a(Linux)) +add_library(mymath STATIC mymath.f90) diff --git a/example/1d-linear-convection/weno3/fortran/FortranLibTest/01/src/mymath.f90 b/example/1d-linear-convection/weno3/fortran/FortranLibTest/01/src/mymath.f90 new file mode 100644 index 00000000..11d02043 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/FortranLibTest/01/src/mymath.f90 @@ -0,0 +1,24 @@ +! 简单的数学库:提供加法和乘法函数 +module mymath_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + private + public :: add, multiply + +contains + + ! 实数加法 + function add(a, b) result(res) + real(real64), intent(in) :: a, b + real(real64) :: res + res = a + b + end function add + + ! 实数乘法 + function multiply(a, b) result(res) + real(real64), intent(in) :: a, b + real(real64) :: res + res = a * b + end function multiply + +end module mymath_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/FortranLibTest/01/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/FortranLibTest/01/tests/CMakeLists.txt new file mode 100644 index 00000000..209e0775 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/FortranLibTest/01/tests/CMakeLists.txt @@ -0,0 +1,7 @@ +# 构建测试可执行文件(依赖mymath库) +add_executable(test_mymath test_mymath.f90) + +# 关键:链接mymath库(自动继承mymath的公共包含目录,无需额外指定模块目录) +target_link_libraries(test_mymath PUBLIC mymath) + + diff --git a/example/1d-linear-convection/weno3/fortran/FortranLibTest/01/tests/test_mymath.f90 b/example/1d-linear-convection/weno3/fortran/FortranLibTest/01/tests/test_mymath.f90 new file mode 100644 index 00000000..ffc5b2b7 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/FortranLibTest/01/tests/test_mymath.f90 @@ -0,0 +1,33 @@ +! 测试程序:调用mymath库的函数,验证功能 +program test_mymath + use iso_fortran_env, only: real64 ! 显式引入,避免模块依赖缺失导致的real64未定义 + use mymath_module + implicit none + real(real64) :: a, b, sum_res, mul_res + + ! 测试数据 + a = 2.5_real64 + b = 4.0_real64 + + ! 调用库函数 + sum_res = add(a, b) + mul_res = multiply(a, b) + + ! 输出结果 + print *, "=== Testing mymath library ===" + print *, "a = ", a + print *, "b = ", b + print *, "a + b = ", sum_res + print *, "a * b = ", mul_res + print *, "=== Test completed ===" + + ! 简单验证(确保结果正确) + if (abs(sum_res - 6.5_real64) < 1e-10_real64 .and. & + abs(mul_res - 10.0_real64) < 1e-10_real64) then + print *, "✓ Test passed!" + else + print *, "✗ Test failed!" + stop 1 + end if + +end program test_mymath \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/FortranLibTest/01a/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/FortranLibTest/01a/CMakeLists.txt new file mode 100644 index 00000000..a31a8fae --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/FortranLibTest/01a/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.18) +project(FortranLibTest VERSION 1.0 LANGUAGES Fortran) + +# 设置Fortran标准 +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +set(CMAKE_Fortran_MODULE_DIRECTORY "${CMAKE_BINARY_DIR}/modules") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +# 添加库目录和测试目录 +add_subdirectory(src) +add_subdirectory(tests) diff --git a/example/1d-linear-convection/weno3/fortran/FortranLibTest/01a/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/FortranLibTest/01a/src/CMakeLists.txt new file mode 100644 index 00000000..29a6e9fe --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/FortranLibTest/01a/src/CMakeLists.txt @@ -0,0 +1,2 @@ +# 构建静态库(核心:生成mymath.lib(Windows)/libmymath.a(Linux)) +add_library(mymath STATIC mymath.f90) diff --git a/example/1d-linear-convection/weno3/fortran/FortranLibTest/01a/src/mymath.f90 b/example/1d-linear-convection/weno3/fortran/FortranLibTest/01a/src/mymath.f90 new file mode 100644 index 00000000..b8379ec7 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/FortranLibTest/01a/src/mymath.f90 @@ -0,0 +1,21 @@ +module mymath_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + private + public :: add, multiply + +contains + + function add(a, b) result(res) + real(real64), intent(in) :: a, b + real(real64) :: res + res = a + b + end function add + + function multiply(a, b) result(res) + real(real64), intent(in) :: a, b + real(real64) :: res + res = a * b + end function multiply + +end module mymath_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/FortranLibTest/01a/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/FortranLibTest/01a/tests/CMakeLists.txt new file mode 100644 index 00000000..209e0775 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/FortranLibTest/01a/tests/CMakeLists.txt @@ -0,0 +1,7 @@ +# 构建测试可执行文件(依赖mymath库) +add_executable(test_mymath test_mymath.f90) + +# 关键:链接mymath库(自动继承mymath的公共包含目录,无需额外指定模块目录) +target_link_libraries(test_mymath PUBLIC mymath) + + diff --git a/example/1d-linear-convection/weno3/fortran/FortranLibTest/01a/tests/test_mymath.f90 b/example/1d-linear-convection/weno3/fortran/FortranLibTest/01a/tests/test_mymath.f90 new file mode 100644 index 00000000..ffc5b2b7 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/FortranLibTest/01a/tests/test_mymath.f90 @@ -0,0 +1,33 @@ +! 测试程序:调用mymath库的函数,验证功能 +program test_mymath + use iso_fortran_env, only: real64 ! 显式引入,避免模块依赖缺失导致的real64未定义 + use mymath_module + implicit none + real(real64) :: a, b, sum_res, mul_res + + ! 测试数据 + a = 2.5_real64 + b = 4.0_real64 + + ! 调用库函数 + sum_res = add(a, b) + mul_res = multiply(a, b) + + ! 输出结果 + print *, "=== Testing mymath library ===" + print *, "a = ", a + print *, "b = ", b + print *, "a + b = ", sum_res + print *, "a * b = ", mul_res + print *, "=== Test completed ===" + + ! 简单验证(确保结果正确) + if (abs(sum_res - 6.5_real64) < 1e-10_real64 .and. & + abs(mul_res - 10.0_real64) < 1e-10_real64) then + print *, "✓ Test passed!" + else + print *, "✗ Test failed!" + stop 1 + end if + +end program test_mymath \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/cfd/01/CMakeLists.txt new file mode 100644 index 00000000..aaa021b3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01/CMakeLists.txt @@ -0,0 +1,27 @@ +# CMakeLists.txt +cmake_minimum_required(VERSION 4.2.1) # 更高版本更好支持Fortran + +project(OneFLOW_CFD LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +# 按依赖顺序显式列出源文件(解决 Intel + VS 编译顺序问题) +set(SOURCES + cfd_solver.f90 +) + +add_executable(oneflow_cfd ${SOURCES}) + +# 包含模块目录(解决 .mod 未找到) +target_include_directories(oneflow_cfd PRIVATE ${CMAKE_Fortran_MODULE_DIRECTORY}) + +# Intel 特定(可选,链接数学库) +if(CMAKE_Fortran_COMPILER_ID MATCHES "Intel") + target_link_libraries(oneflow_cfd m) +endif() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01/cfd_solver.f90 b/example/1d-linear-convection/weno3/fortran/cfd/01/cfd_solver.f90 new file mode 100644 index 00000000..bc4a3366 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01/cfd_solver.f90 @@ -0,0 +1,900 @@ +! OneFLOW-CFD Solver for 1D Convection Equation +! ENO/WENO Reconstruction Comparison - Single File Implementation + +module cfd_solver + use, intrinsic :: iso_fortran_env, only: dp => real64 + implicit none + + private + public :: CfdConfigType, MeshType, ComputationalDomainType, & + SolutionType, CfdType, run_simulation, init_field, & + analytical_solution, performEnoWenoAnalysis + + ! =================================================================== + ! Type Definitions + ! =================================================================== + + ! Configuration parameters + type :: CfdConfigType + character(len=10) :: recon_scheme = "eno" + integer :: flux_type = 0 + integer :: rk_order = 1 + integer :: spatial_order = 3 + real(dp) :: wave_speed = 1.0_dp + real(dp) :: final_time = 0.625_dp + real(dp) :: dt = 0.025_dp + end type CfdConfigType + + ! Mesh definition + type :: MeshType + real(dp) :: xmin = 0.0_dp + real(dp) :: xmax = 2.0_dp + integer :: ncells = 40 + integer :: nnodes + integer :: nx + real(dp) :: L, dx + real(dp), allocatable :: x(:), xcc(:) + contains + procedure :: init => mesh_init + end type MeshType + + ! Computational domain + type :: ComputationalDomainType + type(MeshType) :: mesh + type(CfdConfigType) :: config + integer :: nghosts, ist, ied, ntcells + contains + procedure :: init => domain_init + end type ComputationalDomainType + + ! Solution arrays + type :: SolutionType + type(ComputationalDomainType) :: domain + real(dp), allocatable :: q_face_left(:), q_face_right(:) + real(dp), allocatable :: flux(:), res(:) + real(dp), allocatable :: u(:), un(:) + contains + procedure :: init => solution_init + end type SolutionType + + ! Abstract reconstructor base class + type, abstract :: ReconstructorType + contains + procedure(reconstruct_interface), deferred :: reconstruct + end type ReconstructorType + + abstract interface + subroutine reconstruct_interface(this, q, cfd) + import :: ReconstructorType, CfdType, dp + class(ReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + end subroutine + end interface + + ! ENO reconstructor + type, extends(ReconstructorType) :: EnoReconstructorType + integer :: spatial_order + integer :: ntcells + integer, allocatable :: lmc(:) + real(dp), allocatable :: coef(:,:) + real(dp), allocatable :: dd(:,:) + contains + procedure :: reconstruct => eno_reconstruct + procedure :: init => eno_init + end type EnoReconstructorType + + ! WENO reconstructor + type, extends(ReconstructorType) :: WenoReconstructorType + contains + procedure :: reconstruct => weno_reconstruct + end type WenoReconstructorType + + ! Main CFD solver class + type :: CfdType + type(CfdConfigType) :: config + type(ComputationalDomainType) :: domain + type(SolutionType) :: solution + class(ReconstructorType), allocatable :: reconstructor + contains + procedure :: init => cfd_init + end type CfdType + + ! =================================================================== + ! Module Variables + ! =================================================================== + real(dp), parameter :: eps_weno = 1.0e-6_dp + +contains + + ! =================================================================== + ! Initialization Methods + ! =================================================================== + + ! Mesh initialization + subroutine mesh_init(this) + class(MeshType), intent(inout) :: this + integer :: i + + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, dp) + + allocate(this%x(this%nnodes), this%xcc(this%ncells)) + + ! Node coordinates + do i = 1, this%nnodes + this%x(i) = this%xmin + (i-1) * this%dx + end do + + ! Cell center coordinates + do i = 1, this%ncells + this%xcc(i) = 0.5_dp * (this%x(i) + this%x(i+1)) + end do + end subroutine mesh_init + + ! Domain initialization + subroutine domain_init(this, mesh, config) + class(ComputationalDomainType), intent(inout) :: this + type(MeshType), intent(in) :: mesh + type(CfdConfigType), intent(in) :: config + + this%mesh = mesh + this%config = config + + ! Calculate ghost cells + if (trim(config%recon_scheme) == "eno") then + this%nghosts = config%spatial_order + else if (trim(config%recon_scheme) == "weno") then + this%nghosts = config%spatial_order / 2 + 1 + else + error stop "Unknown reconstruction scheme" + end if + + this%ist = this%nghosts + 1 + this%ied = this%ist + mesh%ncells - 1 + this%ntcells = mesh%ncells + 2 * this%nghosts + + print *, "Domain initialized:" + print *, " mesh.ncells = ", mesh%ncells + print *, " spatial_order = ", config%spatial_order + print *, " nghosts = ", this%nghosts + print *, " ist = ", this%ist, ", ied = ", this%ied + end subroutine domain_init + + ! Solution initialization + subroutine solution_init(this, domain) + class(SolutionType), intent(inout) :: this + type(ComputationalDomainType), intent(in) :: domain + + this%domain = domain + + allocate(this%q_face_left(domain%mesh%nnodes)) + allocate(this%q_face_right(domain%mesh%nnodes)) + allocate(this%flux(domain%mesh%nnodes)) + allocate(this%res(domain%mesh%ncells)) + allocate(this%u(domain%ntcells)) + allocate(this%un(domain%ntcells)) + + this%q_face_left = 0.0_dp + this%q_face_right = 0.0_dp + this%flux = 0.0_dp + this%res = 0.0_dp + this%u = 0.0_dp + this%un = 0.0_dp + end subroutine solution_init + + ! ENO reconstructor initialization + subroutine eno_init(this, spatial_order, ntcells) + class(EnoReconstructorType), intent(inout) :: this + integer, intent(in) :: spatial_order + integer, intent(in) :: ntcells + + this%spatial_order = spatial_order + this%ntcells = ntcells + + allocate(this%lmc(ntcells)) + allocate(this%coef(spatial_order+1, spatial_order)) + allocate(this%dd(spatial_order, ntcells)) + + this%lmc = 0 + this%coef = 0.0_dp + this%dd = 0.0_dp + + ! Initialize coefficients + call init_coef(spatial_order, this%coef) + end subroutine eno_init + + ! CFD solver initialization + subroutine cfd_init(this, config, domain) + class(CfdType), intent(inout) :: this + type(CfdConfigType), intent(in) :: config + type(ComputationalDomainType), intent(in) :: domain + + this%config = config + this%domain = domain + call this%solution%init(domain) + + ! Create reconstructor based on scheme + if (trim(config%recon_scheme) == "eno") then + allocate(EnoReconstructorType :: this%reconstructor) + select type(rec => this%reconstructor) + type is (EnoReconstructorType) + call rec%init(config%spatial_order, domain%ntcells) + end select + else if (trim(config%recon_scheme) == "weno") then + allocate(WenoReconstructorType :: this%reconstructor) + else + error stop "Unknown reconstruction scheme" + end if + end subroutine cfd_init + + ! =================================================================== + ! Initial Conditions and Analytical Solution + ! =================================================================== + + ! Initial condition: step function + function initial_condition(x) result(u0) + real(dp), intent(in) :: x + real(dp) :: u0 + + if (0.5_dp <= x .and. x <= 1.0_dp) then + u0 = 2.0_dp + else + u0 = 1.0_dp + end if + end function initial_condition + + ! Analytical solution with periodic BC + function analytical_solution(x, t, a, L) result(u) + real(dp), intent(in) :: x, t, a, L + real(dp) :: u, x_shifted + + x_shifted = mod(x - a * t + L, L) + u = initial_condition(x_shifted) + end function analytical_solution + + ! Initialize field with step function + subroutine init_field(cfd) + type(CfdType), intent(inout) :: cfd + type(ComputationalDomainType) :: domain + type(SolutionType) :: solution + integer :: i, j + + domain = cfd%domain + solution = cfd%solution + + do i = domain%ist, domain%ied + j = i - domain%ist + 1 + if (0.5_dp <= domain%mesh%xcc(j) .and. domain%mesh%xcc(j) <= 1.0_dp) then + solution%u(i) = 2.0_dp + else + solution%u(i) = 1.0_dp + end if + end do + + call boundary(solution%u, cfd) + call update_oldfield(solution%un, solution%u, domain%ntcells) + end subroutine init_field + + ! =================================================================== + ! Boundary Conditions + ! =================================================================== + + ! Periodic boundary conditions + subroutine periodic_boundary(u, cfd) + real(dp), intent(inout) :: u(:) + type(CfdType), intent(in) :: cfd + type(ComputationalDomainType) :: domain + integer :: ig + + domain = cfd%domain + + ! Left ghost cells = right interior cells + do ig = 1, domain%nghosts + u(domain%ist - ig) = u(domain%ied - ig) + end do + + ! Right ghost cells = left interior cells + do ig = 1, domain%nghosts + u(domain%ied + ig) = u(domain%ist + ig - 1) + end do + end subroutine periodic_boundary + + ! Boundary condition wrapper + subroutine boundary(u, cfd) + real(dp), intent(inout) :: u(:) + type(CfdType), intent(in) :: cfd + + call periodic_boundary(u, cfd) + end subroutine boundary + + ! =================================================================== + ! Reconstruction Methods + ! =================================================================== + + ! Initialize ENO/WENO coefficients + subroutine init_coef(spatial_order, coef) + integer, intent(in) :: spatial_order + real(dp), intent(out) :: coef(:,:) + + coef = 0.0_dp + + select case(spatial_order) + case(1) + coef(1,1) = 1.0_dp + coef(2,1) = 1.0_dp + + case(2) + coef(1,1:2) = [ 3.0_dp/2.0_dp, -1.0_dp/2.0_dp ] + coef(2,1:2) = [ 1.0_dp/2.0_dp, 1.0_dp/2.0_dp ] + coef(3,1:2) = [ -1.0_dp/2.0_dp, 3.0_dp/2.0_dp ] + + case(3) + coef(1,1:3) = [ 11.0_dp/6.0_dp, -7.0_dp/6.0_dp, 1.0_dp/3.0_dp ] + coef(2,1:3) = [ 1.0_dp/3.0_dp, 5.0_dp/6.0_dp, -1.0_dp/6.0_dp ] + coef(3,1:3) = [ -1.0_dp/6.0_dp, 5.0_dp/6.0_dp, 1.0_dp/3.0_dp ] + coef(4,1:3) = [ 1.0_dp/3.0_dp, -7.0_dp/6.0_dp, 11.0_dp/6.0_dp ] + + case(4) + coef(1,1:4) = [ 25.0_dp/12.0_dp, -23.0_dp/12.0_dp, 13.0_dp/12.0_dp, -1.0_dp/4.0_dp ] + coef(2,1:4) = [ 1.0_dp/4.0_dp, 13.0_dp/12.0_dp, -5.0_dp/12.0_dp, 1.0_dp/12.0_dp ] + coef(3,1:4) = [ -1.0_dp/12.0_dp, 7.0_dp/12.0_dp, 7.0_dp/12.0_dp, -1.0_dp/12.0_dp ] + coef(4,1:4) = [ 1.0_dp/12.0_dp, -5.0_dp/12.0_dp, 13.0_dp/12.0_dp, 1.0_dp/4.0_dp ] + coef(5,1:4) = [ -1.0_dp/4.0_dp, 13.0_dp/12.0_dp, -23.0_dp/12.0_dp, 25.0_dp/12.0_dp ] + + case default + error stop "Unsupported spatial order" + end select + end subroutine init_coef + + ! ENO reconstruction + subroutine eno_reconstruct(this, q, cfd) + class(EnoReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + type(ComputationalDomainType) :: domain + type(SolutionType) :: solution + integer :: i, j, m, k1, k2, r1, r2 + + domain = cfd%domain + solution = cfd%solution + + ! Compute divided differences + this%dd(1, :) = q + + do m = 2, this%spatial_order + do j = 1, this%ntcells - m + 1 + this%dd(m, j) = this%dd(m-1, j+1) - this%dd(m-1, j) + end do + end do + + ! Select left-biased stencil for each node + do i = domain%ist-1, domain%ied + this%lmc(i) = i + do m = 2, this%spatial_order + if (abs(this%dd(m, this%lmc(i)-1)) < abs(this%dd(m, this%lmc(i)))) then + this%lmc(i) = this%lmc(i) - 1 + end if + end do + end do + + ! Reconstruct values at cell interfaces + do i = domain%ist, domain%ied + j = i - domain%ist + 1 + k1 = this%lmc(i-1) + k2 = this%lmc(i) + r1 = i-1 - k1 + r2 = i - k2 + + solution%q_face_left(j) = 0.0_dp + solution%q_face_right(j) = 0.0_dp + + do m = 1, this%spatial_order + solution%q_face_left(j) = solution%q_face_left(j) + & + q(k1 + m - 1) * this%coef(r1+2, m) + solution%q_face_right(j) = solution%q_face_right(j) + & + q(k2 + m - 1) * this%coef(r2+1, m) + end do + end do + end subroutine eno_reconstruct + + ! WENO-3 nonlinear weights for left-biased stencil + function wc3L(v1, v2, v3) result(f) + real(dp), intent(in) :: v1, v2, v3 + real(dp) :: f, s0, s1, d0, d1, c0, c1, w0, w1, q0, q1 + + ! Smoothness indicators + s0 = (v3 - v2)**2 + s1 = (v2 - v1)**2 + + ! Nonlinear weights + d0 = 2.0_dp/3.0_dp + d1 = 1.0_dp/3.0_dp + + c0 = d0 / ((eps_weno + s0)**2) + c1 = d1 / ((eps_weno + s1)**2) + + w0 = c0 / (c0 + c1) + w1 = c1 / (c0 + c1) + + ! Candidate stencils + q0 = 0.5_dp * v2 + 0.5_dp * v3 + q1 = -0.5_dp * v1 + 1.5_dp * v2 + + ! Reconstructed value + f = w0 * q0 + w1 * q1 + end function wc3L + + ! WENO-3 nonlinear weights for right-biased stencil + function wc3R(v1, v2, v3) result(f) + real(dp), intent(in) :: v1, v2, v3 + real(dp) :: f, s0, s1, d0, d1, c0, c1, w0, w1, q0, q1 + + ! Smoothness indicators + s0 = (v2 - v1)**2 + s1 = (v3 - v2)**2 + + ! Nonlinear weights + d0 = 2.0_dp/3.0_dp + d1 = 1.0_dp/3.0_dp + + c0 = d0 / ((eps_weno + s0)**2) + c1 = d1 / ((eps_weno + s1)**2) + + w0 = c0 / (c0 + c1) + w1 = c1 / (c0 + c1) + + ! Candidate stencils + q0 = 0.5_dp * v1 + 0.5_dp * v2 + q1 = 1.5_dp * v2 - 0.5_dp * v3 + + ! Reconstructed value + f = w0 * q0 + w1 * q1 + end function wc3R + + ! WENO-3 reconstruction for left interface + subroutine weno3L_periodic(cfd, u, f) + type(CfdType), intent(in) :: cfd + real(dp), intent(in) :: u(:) + real(dp), intent(out) :: f(:) + + type(ComputationalDomainType) :: domain + integer :: i, j + + domain = cfd%domain + + do i = domain%ist-1, domain%ied-1 + j = i - (domain%ist - 1) + f(j) = wc3L(u(i-1), u(i), u(i+1)) + end do + end subroutine weno3L_periodic + + ! WENO-3 reconstruction for right interface + subroutine weno3R_periodic(cfd, u, f) + type(CfdType), intent(in) :: cfd + real(dp), intent(in) :: u(:) + real(dp), intent(out) :: f(:) + + type(ComputationalDomainType) :: domain + integer :: i, j + + domain = cfd%domain + + do i = domain%ist, domain%ied + j = i - domain%ist + 1 + f(j) = wc3R(u(i-1), u(i), u(i+1)) + end do + end subroutine weno3R_periodic + + ! WENO reconstruction + subroutine weno_reconstruct(this, q, cfd) + class(WenoReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + call weno3L_periodic(cfd, q, cfd%solution%q_face_left) + call weno3R_periodic(cfd, q, cfd%solution%q_face_right) + end subroutine weno_reconstruct + + ! General reconstruction wrapper + subroutine reconstruction(q, cfd) + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + call cfd%reconstructor%reconstruct(q, cfd) + end subroutine reconstruction + + ! =================================================================== + ! Flux Functions + ! =================================================================== + + ! Rusanov flux + subroutine rusanov_flux(q_face_left, q_face_right, flux, cfd) + real(dp), intent(in) :: q_face_left(:), q_face_right(:) + real(dp), intent(out) :: flux(:) + type(CfdType), intent(in) :: cfd + + integer :: i + real(dp) :: u_L, u_R, F_L, F_R, c_L, c_R, Smax + + c_L = cfd%config%wave_speed + c_R = cfd%config%wave_speed + + do i = 1, cfd%domain%mesh%nnodes + u_L = q_face_left(i) + u_R = q_face_right(i) + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux(i) = 0.5_dp * (F_L + F_R) - 0.5_dp * Smax * (u_R - u_L) + end do + end subroutine rusanov_flux + + ! Engquist-Osher flux + subroutine engquist_osher_flux(q_face_left, q_face_right, flux, cfd) + real(dp), intent(in) :: q_face_left(:), q_face_right(:) + real(dp), intent(out) :: flux(:) + type(CfdType), intent(in) :: cfd + + integer :: i + real(dp) :: c, cp, cm, u_L, u_R + + c = cfd%config%wave_speed + + do i = 1, cfd%domain%mesh%nnodes + cp = 0.5_dp * (c + abs(c)) + cm = 0.5_dp * (c - abs(c)) + u_L = q_face_left(i) + u_R = q_face_right(i) + flux(i) = cp * u_L + cm * u_R + end do + end subroutine engquist_osher_flux + + ! Inviscid flux selection + subroutine inviscid_flux(q_face_left, q_face_right, flux, cfd) + real(dp), intent(in) :: q_face_left(:), q_face_right(:) + real(dp), intent(out) :: flux(:) + type(CfdType), intent(in) :: cfd + + if (cfd%config%flux_type == 0) then + call rusanov_flux(q_face_left, q_face_right, flux, cfd) + else + call engquist_osher_flux(q_face_left, q_face_right, flux, cfd) + end if + end subroutine inviscid_flux + + ! =================================================================== + ! Residual Computation + ! =================================================================== + + ! Compute residual (flux divergence) + subroutine residual(q, cfd) + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + type(SolutionType) :: solution + type(ComputationalDomainType) :: domain + type(MeshType) :: mesh + integer :: i + + solution = cfd%solution + domain = cfd%domain + mesh = domain%mesh + + ! Reconstruction + call reconstruction(q, cfd) + + ! Compute fluxes + call inviscid_flux(solution%q_face_left, solution%q_face_right, & + solution%flux, cfd) + + ! Compute residual + do i = 1, mesh%ncells + solution%res(i) = -(solution%flux(i+1) - solution%flux(i)) / mesh%dx + end do + end subroutine residual + + ! =================================================================== + ! Time Integration + ! =================================================================== + + ! Update old field + subroutine update_oldfield(qn, q, n) + real(dp), intent(out) :: qn(:) + real(dp), intent(in) :: q(:) + integer, intent(in) :: n + + qn(1:n) = q(1:n) + end subroutine update_oldfield + + ! 1st-order Runge-Kutta (Euler) + subroutine runge_kutta_1(cfd) + type(CfdType), intent(inout) :: cfd + + type(SolutionType) :: solution + type(ComputationalDomainType) :: domain + integer :: i, j + real(dp) :: dt + + solution = cfd%solution + domain = cfd%domain + dt = cfd%config%dt + + call residual(solution%u, cfd) + + do i = domain%ist, domain%ied + j = i - domain%ist + 1 + solution%u(i) = solution%u(i) + dt * solution%res(j) + end do + + call boundary(solution%u, cfd) + call update_oldfield(solution%un, solution%u, domain%ntcells) + end subroutine runge_kutta_1 + + ! 2nd-order Runge-Kutta (Heun) + subroutine runge_kutta_2(cfd) + type(CfdType), intent(inout) :: cfd + + type(SolutionType) :: solution + type(ComputationalDomainType) :: domain + integer :: i, j + real(dp) :: dt + + solution = cfd%solution + domain = cfd%domain + dt = cfd%config%dt + + ! Stage 1 + call residual(solution%u, cfd) + do i = domain%ist, domain%ied + j = i - domain%ist + 1 + solution%u(i) = solution%u(i) + dt * solution%res(j) + end do + call boundary(solution%u, cfd) + + ! Stage 2 + call residual(solution%u, cfd) + do i = domain%ist, domain%ied + j = i - domain%ist + 1 + solution%u(i) = 0.5_dp * solution%un(i) + & + 0.5_dp * solution%u(i) + & + 0.5_dp * dt * solution%res(j) + end do + call boundary(solution%u, cfd) + + call update_oldfield(solution%un, solution%u, domain%ntcells) + end subroutine runge_kutta_2 + + ! 3rd-order Runge-Kutta (SSPRK3) + subroutine runge_kutta_3(cfd) + type(CfdType), intent(inout) :: cfd + + type(SolutionType) :: solution + type(ComputationalDomainType) :: domain + integer :: i, j + real(dp) :: dt + + solution = cfd%solution + domain = cfd%domain + dt = cfd%config%dt + + ! Stage 1 + call residual(solution%u, cfd) + do i = domain%ist, domain%ied + j = i - domain%ist + 1 + solution%u(i) = solution%u(i) + dt * solution%res(j) + end do + call boundary(solution%u, cfd) + + ! Stage 2 + call residual(solution%u, cfd) + do i = domain%ist, domain%ied + j = i - domain%ist + 1 + solution%u(i) = 0.75_dp * solution%un(i) + & + 0.25_dp * solution%u(i) + & + 0.25_dp * dt * solution%res(j) + end do + call boundary(solution%u, cfd) + + ! Stage 3 + call residual(solution%u, cfd) + do i = domain%ist, domain%ied + j = i - domain%ist + 1 + solution%u(i) = (1.0_dp/3.0_dp) * solution%un(i) + & + (2.0_dp/3.0_dp) * solution%u(i) + & + (2.0_dp/3.0_dp) * dt * solution%res(j) + end do + call boundary(solution%u, cfd) + + call update_oldfield(solution%un, solution%u, domain%ntcells) + end subroutine runge_kutta_3 + + ! Runge-Kutta selection + subroutine runge_kutta(cfd) + type(CfdType), intent(inout) :: cfd + + select case(cfd%config%rk_order) + case(1) + call runge_kutta_1(cfd) + case(2) + call runge_kutta_2(cfd) + case(3) + call runge_kutta_3(cfd) + case default + call runge_kutta_1(cfd) + end select + end subroutine runge_kutta + + ! =================================================================== + ! Simulation Driver + ! =================================================================== + + ! Run simulation to final time + function run_simulation(cfd, final_time) result(u_result) + type(CfdType), intent(inout) :: cfd + real(dp), intent(in) :: final_time + real(dp), allocatable :: u_result(:) + + type(ComputationalDomainType) :: domain + real(dp) :: t, dt, dt_old + integer :: ncells + + domain = cfd%domain + ncells = domain%mesh%ncells + allocate(u_result(ncells)) + + t = 0.0_dp + dt_old = cfd%config%dt + dt = dt_old + + do while (t < final_time - 1.0e-12_dp) + if (t + dt > final_time) then + dt = final_time - t + end if + cfd%config%dt = dt + call runge_kutta(cfd) + t = t + dt + end do + + cfd%config%dt = dt_old + + ! Extract physical solution (without ghost cells) + u_result = cfd%solution%u(domain%ist:domain%ied) + end function run_simulation + + ! =================================================================== + ! Main Analysis Function + ! =================================================================== + + ! Perform ENO-WENO comparative analysis + subroutine performEnoWenoAnalysis() + type(CfdConfigType) :: config_eno3, config_weno3 + type(MeshType) :: mesh + type(ComputationalDomainType) :: domain_eno3, domain_weno3 + type(CfdType) :: cfd_eno3, cfd_weno3 + real(dp), allocatable :: u_eno(:), u_weno(:), u_analytical(:) + real(dp), allocatable :: xcc(:) + integer :: i, ncells, iunit + + ! Initialize mesh + mesh = MeshType() + call mesh%init() + ncells = mesh%ncells + allocate(xcc(ncells)) + xcc = mesh%xcc + + ! Configure ENO3 + config_eno3%recon_scheme = "eno" + config_eno3%spatial_order = 3 + config_eno3%flux_type = 0 + config_eno3%rk_order = 1 + config_eno3%wave_speed = 1.0_dp + config_eno3%final_time = 0.625_dp + config_eno3%dt = 0.0025_dp + + ! Configure WENO3 + config_weno3%recon_scheme = "weno" + config_weno3%spatial_order = 3 + config_weno3%flux_type = 0 + config_weno3%rk_order = 1 + config_weno3%wave_speed = 1.0_dp + config_weno3%final_time = 0.625_dp + config_weno3%dt = 0.0025_dp + + ! Create domains + domain_eno3 = ComputationalDomainType() + domain_weno3 = ComputationalDomainType() + + call domain_eno3%init(mesh, config_eno3) + call domain_weno3%init(mesh, config_weno3) + + ! Create CFD solvers + cfd_eno3 = CfdType() + cfd_weno3 = CfdType() + + call cfd_eno3%init(config_eno3, domain_eno3) + call cfd_weno3%init(config_weno3, domain_weno3) + + ! Allocate arrays + allocate(u_eno(ncells), u_weno(ncells), u_analytical(ncells)) + + ! Run ENO simulation + print *, "Running ENO3 simulation..." + call init_field(cfd_eno3) + u_eno = run_simulation(cfd_eno3, config_eno3%final_time) + + ! Run WENO simulation + print *, "Running WENO3 simulation..." + call init_field(cfd_weno3) + u_weno = run_simulation(cfd_weno3, config_weno3%final_time) + + ! Compute analytical solution + print *, "Computing analytical solution..." + do i = 1, ncells + u_analytical(i) = analytical_solution(xcc(i), config_weno3%final_time, & + config_weno3%wave_speed, mesh%L) + end do + + ! Write results to files + print *, "Writing results to files..." + + ! Write ENO results + open(newunit=iunit, file='eno_results.txt', status='replace') + write(iunit, '(A)') '# x, u (ENO3)' + do i = 1, ncells + write(iunit, '(2F12.6)') xcc(i), u_eno(i) + end do + close(iunit) + + ! Write WENO results + open(newunit=iunit, file='weno_results.txt', status='replace') + write(iunit, '(A)') '# x, u (WENO3)' + do i = 1, ncells + write(iunit, '(2F12.6)') xcc(i), u_weno(i) + end do + close(iunit) + + ! Write analytical results + open(newunit=iunit, file='analytical_results.txt', status='replace') + write(iunit, '(A)') '# x, u (Analytical)' + do i = 1, ncells + write(iunit, '(2F12.6)') xcc(i), u_analytical(i) + end do + close(iunit) + + print *, "==========================================" + print *, "Simulation completed successfully!" + print *, "Results written to:" + print *, " eno_results.txt" + print *, " weno_results.txt" + print *, " analytical_results.txt" + print *, "" + print *, "To generate the comparison plot, run:" + print *, " python postprocess.py" + print *, "==========================================" + + deallocate(u_eno, u_weno, u_analytical, xcc) + end subroutine performEnoWenoAnalysis + +end module cfd_solver + +! =================================================================== +! Main Program +! =================================================================== +program main + use cfd_solver + implicit none + + print *, "==========================================" + print *, "OneFLOW-CFD Solver for 1D Convection" + print *, "ENO3 vs WENO3 Comparison" + print *, "==========================================" + + call performEnoWenoAnalysis() + + print *, "Program finished successfully!" + +end program main \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01/postprocess.py b/example/1d-linear-convection/weno3/fortran/cfd/01/postprocess.py new file mode 100644 index 00000000..6cbcc4d9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01/postprocess.py @@ -0,0 +1,38 @@ +import numpy as np +import matplotlib.pyplot as plt + +def read_results(filename): + """Read results from Fortran output file""" + data = np.loadtxt(filename, comments='#') + return data[:, 0], data[:, 1] + +def main(): + # Read results + x_eno, u_eno = read_results('eno_results.txt') + x_weno, u_weno = read_results('weno_results.txt') + x_analytical, u_analytical = read_results('analytical_results.txt') + + # Create plot + plt.figure(figsize=(12, 8)) + plt.plot(x_eno, u_eno, 'bo-', linewidth=1.5, markersize=4, + markerfacecolor='none', label='ENO3 (Rusanov)') + plt.plot(x_weno, u_weno, 'gs-', linewidth=1.5, markersize=4, + markerfacecolor='none', label='WENO3 (Rusanov)') + plt.plot(x_analytical, u_analytical, 'r--', linewidth=2, label='Analytical') + + plt.title('1D Convection Equation: ENO3 vs WENO3 Comparison (t=0.625)', fontsize=14) + plt.xlabel('x', fontsize=12) + plt.ylabel('u', fontsize=12) + plt.legend(fontsize=12) + plt.grid(True, alpha=0.3) + plt.tight_layout() + + # Save figure + plt.savefig('eno_weno_comparison.png', dpi=300, bbox_inches='tight') + print("Plot saved as: eno_weno_comparison.png") + + # Show plot + plt.show() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01a/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/cfd/01a/CMakeLists.txt new file mode 100644 index 00000000..aaa021b3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01a/CMakeLists.txt @@ -0,0 +1,27 @@ +# CMakeLists.txt +cmake_minimum_required(VERSION 4.2.1) # 更高版本更好支持Fortran + +project(OneFLOW_CFD LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +# 按依赖顺序显式列出源文件(解决 Intel + VS 编译顺序问题) +set(SOURCES + cfd_solver.f90 +) + +add_executable(oneflow_cfd ${SOURCES}) + +# 包含模块目录(解决 .mod 未找到) +target_include_directories(oneflow_cfd PRIVATE ${CMAKE_Fortran_MODULE_DIRECTORY}) + +# Intel 特定(可选,链接数学库) +if(CMAKE_Fortran_COMPILER_ID MATCHES "Intel") + target_link_libraries(oneflow_cfd m) +endif() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01a/cfd_solver.f90 b/example/1d-linear-convection/weno3/fortran/cfd/01a/cfd_solver.f90 new file mode 100644 index 00000000..8f07a767 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01a/cfd_solver.f90 @@ -0,0 +1,873 @@ +! OneFLOW-CFD Solver for 1D Convection Equation +! ENO/WENO Reconstruction Comparison - Single File Implementation + +module cfd_solver + use, intrinsic :: iso_fortran_env, only: dp => real64 + implicit none + + private + + ! =================================================================== + ! Forward Type Declarations + ! =================================================================== + type, public :: CfdConfigType + character(len=10) :: recon_scheme = "eno" + integer :: flux_type = 0 + integer :: rk_order = 1 + integer :: spatial_order = 3 + real(dp) :: wave_speed = 1.0_dp + real(dp) :: final_time = 0.625_dp + real(dp) :: dt = 0.025_dp + end type CfdConfigType + + type, public :: MeshType + real(dp) :: xmin = 0.0_dp + real(dp) :: xmax = 2.0_dp + integer :: ncells = 40 + integer :: nnodes = 0 + integer :: nx = 0 + real(dp) :: L = 0.0_dp + real(dp) :: dx = 0.0_dp + real(dp), allocatable :: x(:), xcc(:) + contains + procedure :: init => mesh_init + end type MeshType + + type, public :: ComputationalDomainType + type(MeshType) :: mesh + type(CfdConfigType) :: config + integer :: nghosts = 0 + integer :: ist = 0 + integer :: ied = 0 + integer :: ntcells = 0 + contains + procedure :: init => domain_init + end type ComputationalDomainType + + type, public :: SolutionType + type(ComputationalDomainType) :: domain + real(dp), allocatable :: q_face_left(:), q_face_right(:) + real(dp), allocatable :: flux(:), res(:) + real(dp), allocatable :: u(:), un(:) + contains + procedure :: init => solution_init + end type SolutionType + + ! Abstract reconstructor base class with explicit interface + type, abstract, public :: ReconstructorType + contains + procedure(reconstruct_interface), deferred, pass :: reconstruct + end type ReconstructorType + + ! Define abstract interface BEFORE using it + abstract interface + subroutine reconstruct_interface(this, q, cfd) + import :: ReconstructorType, dp + class(ReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + class(*), intent(inout) :: cfd + end subroutine reconstruct_interface + end interface + + ! ENO reconstructor + type, extends(ReconstructorType), public :: EnoReconstructorType + integer :: spatial_order + integer :: ntcells + integer, allocatable :: lmc(:) + real(dp), allocatable :: coef(:,:) + real(dp), allocatable :: dd(:,:) + contains + procedure :: reconstruct => eno_reconstruct + procedure :: init => eno_init + end type EnoReconstructorType + + ! WENO reconstructor + type, extends(ReconstructorType), public :: WenoReconstructorType + contains + procedure :: reconstruct => weno_reconstruct + end type WenoReconstructorType + + ! Main CFD solver class + type, public :: CfdType + type(CfdConfigType) :: config + type(ComputationalDomainType) :: domain + type(SolutionType) :: solution + class(ReconstructorType), allocatable :: reconstructor + contains + procedure :: init => cfd_init + end type CfdType + + ! =================================================================== + ! Public Procedures + ! =================================================================== + public :: run_simulation, init_field, analytical_solution, performEnoWenoAnalysis + + ! =================================================================== + ! Module Variables + ! =================================================================== + real(dp), parameter :: eps_weno = 1.0e-6_dp + +contains + + ! =================================================================== + ! Initialization Methods + ! =================================================================== + + ! Mesh initialization + subroutine mesh_init(this) + class(MeshType), intent(inout) :: this + integer :: i + + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, dp) + + allocate(this%x(this%nnodes), this%xcc(this%ncells)) + + ! Node coordinates + do i = 1, this%nnodes + this%x(i) = this%xmin + (i-1) * this%dx + end do + + ! Cell center coordinates + do i = 1, this%ncells + this%xcc(i) = 0.5_dp * (this%x(i) + this%x(i+1)) + end do + end subroutine mesh_init + + ! Domain initialization + subroutine domain_init(this, mesh, config) + class(ComputationalDomainType), intent(inout) :: this + type(MeshType), intent(in) :: mesh + type(CfdConfigType), intent(in) :: config + + this%mesh = mesh + this%config = config + + ! Calculate ghost cells + if (trim(config%recon_scheme) == "eno") then + this%nghosts = config%spatial_order + else if (trim(config%recon_scheme) == "weno") then + this%nghosts = config%spatial_order / 2 + 1 + else + error stop "Unknown reconstruction scheme" + end if + + this%ist = this%nghosts + 1 + this%ied = this%ist + mesh%ncells - 1 + this%ntcells = mesh%ncells + 2 * this%nghosts + + print *, "Domain initialized:" + print *, " mesh.ncells = ", mesh%ncells + print *, " spatial_order = ", config%spatial_order + print *, " nghosts = ", this%nghosts + print *, " ist = ", this%ist, ", ied = ", this%ied + end subroutine domain_init + + ! Solution initialization + subroutine solution_init(this, domain) + class(SolutionType), intent(inout) :: this + type(ComputationalDomainType), intent(in) :: domain + + this%domain = domain + + allocate(this%q_face_left(domain%mesh%nnodes)) + allocate(this%q_face_right(domain%mesh%nnodes)) + allocate(this%flux(domain%mesh%nnodes)) + allocate(this%res(domain%mesh%ncells)) + allocate(this%u(domain%ntcells)) + allocate(this%un(domain%ntcells)) + + this%q_face_left = 0.0_dp + this%q_face_right = 0.0_dp + this%flux = 0.0_dp + this%res = 0.0_dp + this%u = 0.0_dp + this%un = 0.0_dp + end subroutine solution_init + + ! ENO reconstructor initialization + subroutine eno_init(this, spatial_order, ntcells) + class(EnoReconstructorType), intent(inout) :: this + integer, intent(in) :: spatial_order + integer, intent(in) :: ntcells + + this%spatial_order = spatial_order + this%ntcells = ntcells + + allocate(this%lmc(ntcells)) + allocate(this%coef(spatial_order+1, spatial_order)) + allocate(this%dd(spatial_order, ntcells)) + + this%lmc = 0 + this%coef = 0.0_dp + this%dd = 0.0_dp + + ! Initialize coefficients + call init_coef(spatial_order, this%coef) + end subroutine eno_init + + ! CFD solver initialization + subroutine cfd_init(this, config, domain) + class(CfdType), intent(inout) :: this + type(CfdConfigType), intent(in) :: config + type(ComputationalDomainType), intent(in) :: domain + + this%config = config + this%domain = domain + call this%solution%init(domain) + + ! Create reconstructor based on scheme + if (trim(config%recon_scheme) == "eno") then + allocate(EnoReconstructorType :: this%reconstructor) + select type(rec => this%reconstructor) + type is (EnoReconstructorType) + call rec%init(config%spatial_order, domain%ntcells) + end select + else if (trim(config%recon_scheme) == "weno") then + allocate(WenoReconstructorType :: this%reconstructor) + else + error stop "Unknown reconstruction scheme" + end if + end subroutine cfd_init + + ! =================================================================== + ! Initial Conditions and Analytical Solution + ! =================================================================== + + ! Initial condition: step function + function initial_condition(x) result(u0) + real(dp), intent(in) :: x + real(dp) :: u0 + + if (0.5_dp <= x .and. x <= 1.0_dp) then + u0 = 2.0_dp + else + u0 = 1.0_dp + end if + end function initial_condition + + ! Analytical solution with periodic BC + function analytical_solution(x, t, a, L) result(u) + real(dp), intent(in) :: x, t, a, L + real(dp) :: u, x_shifted + + x_shifted = mod(x - a * t + L, L) + u = initial_condition(x_shifted) + end function analytical_solution + + ! Initialize field with step function + subroutine init_field(cfd) + type(CfdType), intent(inout) :: cfd + integer :: i, j + + do i = cfd%domain%ist, cfd%domain%ied + j = i - cfd%domain%ist + 1 + if (0.5_dp <= cfd%domain%mesh%xcc(j) .and. cfd%domain%mesh%xcc(j) <= 1.0_dp) then + cfd%solution%u(i) = 2.0_dp + else + cfd%solution%u(i) = 1.0_dp + end if + end do + + call boundary(cfd%solution%u, cfd) + call update_oldfield(cfd%solution%un, cfd%solution%u, cfd%domain%ntcells) + end subroutine init_field + + ! =================================================================== + ! Boundary Conditions + ! =================================================================== + + ! Periodic boundary conditions + subroutine periodic_boundary(u, cfd) + real(dp), intent(inout) :: u(:) + type(CfdType), intent(in) :: cfd + integer :: ig + + ! Left ghost cells = right interior cells + do ig = 1, cfd%domain%nghosts + u(cfd%domain%ist - ig) = u(cfd%domain%ied - ig) + end do + + ! Right ghost cells = left interior cells + do ig = 1, cfd%domain%nghosts + u(cfd%domain%ied + ig) = u(cfd%domain%ist + ig - 1) + end do + end subroutine periodic_boundary + + ! Boundary condition wrapper + subroutine boundary(u, cfd) + real(dp), intent(inout) :: u(:) + type(CfdType), intent(in) :: cfd + + call periodic_boundary(u, cfd) + end subroutine boundary + + ! =================================================================== + ! Reconstruction Methods + ! =================================================================== + + ! Initialize ENO/WENO coefficients + subroutine init_coef(spatial_order, coef) + integer, intent(in) :: spatial_order + real(dp), intent(out) :: coef(:,:) + + coef = 0.0_dp + + select case(spatial_order) + case(1) + coef(1,1) = 1.0_dp + coef(2,1) = 1.0_dp + + case(2) + coef(1,1:2) = [ 3.0_dp/2.0_dp, -1.0_dp/2.0_dp ] + coef(2,1:2) = [ 1.0_dp/2.0_dp, 1.0_dp/2.0_dp ] + coef(3,1:2) = [ -1.0_dp/2.0_dp, 3.0_dp/2.0_dp ] + + case(3) + coef(1,1:3) = [ 11.0_dp/6.0_dp, -7.0_dp/6.0_dp, 1.0_dp/3.0_dp ] + coef(2,1:3) = [ 1.0_dp/3.0_dp, 5.0_dp/6.0_dp, -1.0_dp/6.0_dp ] + coef(3,1:3) = [ -1.0_dp/6.0_dp, 5.0_dp/6.0_dp, 1.0_dp/3.0_dp ] + coef(4,1:3) = [ 1.0_dp/3.0_dp, -7.0_dp/6.0_dp, 11.0_dp/6.0_dp ] + + case(4) + coef(1,1:4) = [ 25.0_dp/12.0_dp, -23.0_dp/12.0_dp, 13.0_dp/12.0_dp, -1.0_dp/4.0_dp ] + coef(2,1:4) = [ 1.0_dp/4.0_dp, 13.0_dp/12.0_dp, -5.0_dp/12.0_dp, 1.0_dp/12.0_dp ] + coef(3,1:4) = [ -1.0_dp/12.0_dp, 7.0_dp/12.0_dp, 7.0_dp/12.0_dp, -1.0_dp/12.0_dp ] + coef(4,1:4) = [ 1.0_dp/12.0_dp, -5.0_dp/12.0_dp, 13.0_dp/12.0_dp, 1.0_dp/4.0_dp ] + coef(5,1:4) = [ -1.0_dp/4.0_dp, 13.0_dp/12.0_dp, -23.0_dp/12.0_dp, 25.0_dp/12.0_dp ] + + case default + error stop "Unsupported spatial order" + end select + end subroutine init_coef + + ! ENO reconstruction + subroutine eno_reconstruct(this, q, cfd_obj) + class(EnoReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + class(*), intent(inout) :: cfd_obj + + type(CfdType), pointer :: cfd + integer :: i, j, m, k1, k2, r1, r2 + + ! Cast to CfdType + select type(cfd_obj) + type is (CfdType) + cfd => cfd_obj + class default + error stop "Invalid CFD object type in eno_reconstruct" + end select + + ! Compute divided differences + this%dd(1, :) = q + + do m = 2, this%spatial_order + do j = 1, this%ntcells - m + 1 + this%dd(m, j) = this%dd(m-1, j+1) - this%dd(m-1, j) + end do + end do + + ! Select left-biased stencil for each node + do i = cfd%domain%ist-1, cfd%domain%ied + this%lmc(i) = i + do m = 2, this%spatial_order + if (abs(this%dd(m, this%lmc(i)-1)) < abs(this%dd(m, this%lmc(i)))) then + this%lmc(i) = this%lmc(i) - 1 + end if + end do + end do + + ! Reconstruct values at cell interfaces + do i = cfd%domain%ist, cfd%domain%ied + j = i - cfd%domain%ist + 1 + k1 = this%lmc(i-1) + k2 = this%lmc(i) + r1 = i-1 - k1 + r2 = i - k2 + + cfd%solution%q_face_left(j) = 0.0_dp + cfd%solution%q_face_right(j) = 0.0_dp + + do m = 1, this%spatial_order + cfd%solution%q_face_left(j) = cfd%solution%q_face_left(j) + & + q(k1 + m - 1) * this%coef(r1+2, m) + cfd%solution%q_face_right(j) = cfd%solution%q_face_right(j) + & + q(k2 + m - 1) * this%coef(r2+1, m) + end do + end do + end subroutine eno_reconstruct + + ! WENO-3 nonlinear weights for left-biased stencil + function wc3L(v1, v2, v3) result(f) + real(dp), intent(in) :: v1, v2, v3 + real(dp) :: f, s0, s1, d0, d1, c0, c1, w0, w1, q0, q1 + + ! Smoothness indicators + s0 = (v3 - v2)**2 + s1 = (v2 - v1)**2 + + ! Nonlinear weights + d0 = 2.0_dp/3.0_dp + d1 = 1.0_dp/3.0_dp + + c0 = d0 / ((eps_weno + s0)**2) + c1 = d1 / ((eps_weno + s1)**2) + + w0 = c0 / (c0 + c1) + w1 = c1 / (c0 + c1) + + ! Candidate stencils + q0 = 0.5_dp * v2 + 0.5_dp * v3 + q1 = -0.5_dp * v1 + 1.5_dp * v2 + + ! Reconstructed value + f = w0 * q0 + w1 * q1 + end function wc3L + + ! WENO-3 nonlinear weights for right-biased stencil + function wc3R(v1, v2, v3) result(f) + real(dp), intent(in) :: v1, v2, v3 + real(dp) :: f, s0, s1, d0, d1, c0, c1, w0, w1, q0, q1 + + ! Smoothness indicators + s0 = (v2 - v1)**2 + s1 = (v3 - v2)**2 + + ! Nonlinear weights + d0 = 2.0_dp/3.0_dp + d1 = 1.0_dp/3.0_dp + + c0 = d0 / ((eps_weno + s0)**2) + c1 = d1 / ((eps_weno + s1)**2) + + w0 = c0 / (c0 + c1) + w1 = c1 / (c0 + c1) + + ! Candidate stencils + q0 = 0.5_dp * v1 + 0.5_dp * v2 + q1 = 1.5_dp * v2 - 0.5_dp * v3 + + ! Reconstructed value + f = w0 * q0 + w1 * q1 + end function wc3R + + ! WENO-3 reconstruction for left interface + subroutine weno3L_periodic(cfd, u, f) + type(CfdType), intent(in) :: cfd + real(dp), intent(in) :: u(:) + real(dp), intent(out) :: f(:) + + integer :: i, j + + do i = cfd%domain%ist-1, cfd%domain%ied-1 + j = i - (cfd%domain%ist - 1) + f(j) = wc3L(u(i-1), u(i), u(i+1)) + end do + end subroutine weno3L_periodic + + ! WENO-3 reconstruction for right interface + subroutine weno3R_periodic(cfd, u, f) + type(CfdType), intent(in) :: cfd + real(dp), intent(in) :: u(:) + real(dp), intent(out) :: f(:) + + integer :: i, j + + do i = cfd%domain%ist, cfd%domain%ied + j = i - cfd%domain%ist + 1 + f(j) = wc3R(u(i-1), u(i), u(i+1)) + end do + end subroutine weno3R_periodic + + ! WENO reconstruction + subroutine weno_reconstruct(this, q, cfd_obj) + class(WenoReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + class(*), intent(inout) :: cfd_obj + + type(CfdType), pointer :: cfd + + ! Cast to CfdType + select type(cfd_obj) + type is (CfdType) + cfd => cfd_obj + class default + error stop "Invalid CFD object type in weno_reconstruct" + end select + + call weno3L_periodic(cfd, q, cfd%solution%q_face_left) + call weno3R_periodic(cfd, q, cfd%solution%q_face_right) + end subroutine weno_reconstruct + + ! General reconstruction wrapper + subroutine reconstruction(q, cfd) + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + call cfd%reconstructor%reconstruct(q, cfd) + end subroutine reconstruction + + ! =================================================================== + ! Flux Functions + ! =================================================================== + + ! Rusanov flux + subroutine rusanov_flux(q_face_left, q_face_right, flux, cfd) + real(dp), intent(in) :: q_face_left(:), q_face_right(:) + real(dp), intent(out) :: flux(:) + type(CfdType), intent(in) :: cfd + + integer :: i + real(dp) :: u_L, u_R, F_L, F_R, c_L, c_R, Smax + + c_L = cfd%config%wave_speed + c_R = cfd%config%wave_speed + + do i = 1, cfd%domain%mesh%nnodes + u_L = q_face_left(i) + u_R = q_face_right(i) + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux(i) = 0.5_dp * (F_L + F_R) - 0.5_dp * Smax * (u_R - u_L) + end do + end subroutine rusanov_flux + + ! Engquist-Osher flux + subroutine engquist_osher_flux(q_face_left, q_face_right, flux, cfd) + real(dp), intent(in) :: q_face_left(:), q_face_right(:) + real(dp), intent(out) :: flux(:) + type(CfdType), intent(in) :: cfd + + integer :: i + real(dp) :: c, cp, cm, u_L, u_R + + c = cfd%config%wave_speed + + do i = 1, cfd%domain%mesh%nnodes + cp = 0.5_dp * (c + abs(c)) + cm = 0.5_dp * (c - abs(c)) + u_L = q_face_left(i) + u_R = q_face_right(i) + flux(i) = cp * u_L + cm * u_R + end do + end subroutine engquist_osher_flux + + ! Inviscid flux selection + subroutine inviscid_flux(q_face_left, q_face_right, flux, cfd) + real(dp), intent(in) :: q_face_left(:), q_face_right(:) + real(dp), intent(out) :: flux(:) + type(CfdType), intent(in) :: cfd + + if (cfd%config%flux_type == 0) then + call rusanov_flux(q_face_left, q_face_right, flux, cfd) + else + call engquist_osher_flux(q_face_left, q_face_right, flux, cfd) + end if + end subroutine inviscid_flux + + ! =================================================================== + ! Residual Computation + ! =================================================================== + + ! Compute residual (flux divergence) + subroutine residual(q, cfd) + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + integer :: i + + ! Reconstruction + call reconstruction(q, cfd) + + ! Compute fluxes + call inviscid_flux(cfd%solution%q_face_left, cfd%solution%q_face_right, & + cfd%solution%flux, cfd) + + ! Compute residual + do i = 1, cfd%domain%mesh%ncells + cfd%solution%res(i) = -(cfd%solution%flux(i+1) - cfd%solution%flux(i)) / & + cfd%domain%mesh%dx + end do + end subroutine residual + + ! =================================================================== + ! Time Integration + ! =================================================================== + + ! Update old field + subroutine update_oldfield(qn, q, n) + real(dp), intent(out) :: qn(:) + real(dp), intent(in) :: q(:) + integer, intent(in) :: n + + qn(1:n) = q(1:n) + end subroutine update_oldfield + + ! 1st-order Runge-Kutta (Euler) + subroutine runge_kutta_1(cfd) + type(CfdType), intent(inout) :: cfd + + integer :: i, j + real(dp) :: dt + + dt = cfd%config%dt + + call residual(cfd%solution%u, cfd) + + do i = cfd%domain%ist, cfd%domain%ied + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = cfd%solution%u(i) + dt * cfd%solution%res(j) + end do + + call boundary(cfd%solution%u, cfd) + call update_oldfield(cfd%solution%un, cfd%solution%u, cfd%domain%ntcells) + end subroutine runge_kutta_1 + + ! 2nd-order Runge-Kutta (Heun) + subroutine runge_kutta_2(cfd) + type(CfdType), intent(inout) :: cfd + + integer :: i, j + real(dp) :: dt + + dt = cfd%config%dt + + ! Stage 1 + call residual(cfd%solution%u, cfd) + do i = cfd%domain%ist, cfd%domain%ied + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = cfd%solution%u(i) + dt * cfd%solution%res(j) + end do + call boundary(cfd%solution%u, cfd) + + ! Stage 2 + call residual(cfd%solution%u, cfd) + do i = cfd%domain%ist, cfd%domain%ied + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = 0.5_dp * cfd%solution%un(i) + & + 0.5_dp * cfd%solution%u(i) + & + 0.5_dp * dt * cfd%solution%res(j) + end do + call boundary(cfd%solution%u, cfd) + + call update_oldfield(cfd%solution%un, cfd%solution%u, cfd%domain%ntcells) + end subroutine runge_kutta_2 + + ! 3rd-order Runge-Kutta (SSPRK3) + subroutine runge_kutta_3(cfd) + type(CfdType), intent(inout) :: cfd + + integer :: i, j + real(dp) :: dt + + dt = cfd%config%dt + + ! Stage 1 + call residual(cfd%solution%u, cfd) + do i = cfd%domain%ist, cfd%domain%ied + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = cfd%solution%u(i) + dt * cfd%solution%res(j) + end do + call boundary(cfd%solution%u, cfd) + + ! Stage 2 + call residual(cfd%solution%u, cfd) + do i = cfd%domain%ist, cfd%domain%ied + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = 0.75_dp * cfd%solution%un(i) + & + 0.25_dp * cfd%solution%u(i) + & + 0.25_dp * dt * cfd%solution%res(j) + end do + call boundary(cfd%solution%u, cfd) + + ! Stage 3 + call residual(cfd%solution%u, cfd) + do i = cfd%domain%ist, cfd%domain%ied + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = (1.0_dp/3.0_dp) * cfd%solution%un(i) + & + (2.0_dp/3.0_dp) * cfd%solution%u(i) + & + (2.0_dp/3.0_dp) * dt * cfd%solution%res(j) + end do + call boundary(cfd%solution%u, cfd) + + call update_oldfield(cfd%solution%un, cfd%solution%u, cfd%domain%ntcells) + end subroutine runge_kutta_3 + + ! Runge-Kutta selection + subroutine runge_kutta(cfd) + type(CfdType), intent(inout) :: cfd + + select case(cfd%config%rk_order) + case(1) + call runge_kutta_1(cfd) + case(2) + call runge_kutta_2(cfd) + case(3) + call runge_kutta_3(cfd) + case default + call runge_kutta_1(cfd) + end select + end subroutine runge_kutta + + ! =================================================================== + ! Simulation Driver + ! =================================================================== + + ! Run simulation to final time + function run_simulation(cfd, final_time) result(u_result) + type(CfdType), intent(inout) :: cfd + real(dp), intent(in) :: final_time + real(dp), allocatable :: u_result(:) + + real(dp) :: t, dt, dt_old + + allocate(u_result(cfd%domain%mesh%ncells)) + + t = 0.0_dp + dt_old = cfd%config%dt + dt = dt_old + + do while (t < final_time - 1.0e-12_dp) + if (t + dt > final_time) then + dt = final_time - t + end if + cfd%config%dt = dt + call runge_kutta(cfd) + t = t + dt + end do + + cfd%config%dt = dt_old + + ! Extract physical solution (without ghost cells) + u_result = cfd%solution%u(cfd%domain%ist:cfd%domain%ied) + end function run_simulation + + ! =================================================================== + ! Main Analysis Function + ! =================================================================== + + ! Perform ENO-WENO comparative analysis + subroutine performEnoWenoAnalysis() + type(CfdConfigType) :: config_eno3, config_weno3 + type(MeshType) :: mesh + type(ComputationalDomainType) :: domain_eno3, domain_weno3 + type(CfdType) :: cfd_eno3, cfd_weno3 + real(dp), allocatable :: u_eno(:), u_weno(:), u_analytical(:) + real(dp), allocatable :: xcc(:) + integer :: i, ncells, iunit + + ! Initialize mesh + call mesh%init() + ncells = mesh%ncells + allocate(xcc(ncells)) + xcc = mesh%xcc + + ! Configure ENO3 + config_eno3%recon_scheme = "eno" + config_eno3%spatial_order = 3 + config_eno3%flux_type = 0 + config_eno3%rk_order = 1 + config_eno3%wave_speed = 1.0_dp + config_eno3%final_time = 0.625_dp + config_eno3%dt = 0.0025_dp + + ! Configure WENO3 + config_weno3%recon_scheme = "weno" + config_weno3%spatial_order = 3 + config_weno3%flux_type = 0 + config_weno3%rk_order = 1 + config_weno3%wave_speed = 1.0_dp + config_weno3%final_time = 0.625_dp + config_weno3%dt = 0.0025_dp + + ! Create domains + call domain_eno3%init(mesh, config_eno3) + call domain_weno3%init(mesh, config_weno3) + + ! Create CFD solvers + call cfd_eno3%init(config_eno3, domain_eno3) + call cfd_weno3%init(config_weno3, domain_weno3) + + ! Allocate arrays + allocate(u_eno(ncells), u_weno(ncells), u_analytical(ncells)) + + ! Run ENO simulation + print *, "Running ENO3 simulation..." + call init_field(cfd_eno3) + u_eno = run_simulation(cfd_eno3, config_eno3%final_time) + + ! Run WENO simulation + print *, "Running WENO3 simulation..." + call init_field(cfd_weno3) + u_weno = run_simulation(cfd_weno3, config_weno3%final_time) + + ! Compute analytical solution + print *, "Computing analytical solution..." + do i = 1, ncells + u_analytical(i) = analytical_solution(xcc(i), config_weno3%final_time, & + config_weno3%wave_speed, mesh%L) + end do + + ! Write results to files + print *, "Writing results to files..." + + ! Write ENO results + open(newunit=iunit, file='eno_results.txt', status='replace') + write(iunit, '(A)') '# x, u (ENO3)' + do i = 1, ncells + write(iunit, '(2F12.6)') xcc(i), u_eno(i) + end do + close(iunit) + + ! Write WENO results + open(newunit=iunit, file='weno_results.txt', status='replace') + write(iunit, '(A)') '# x, u (WENO3)' + do i = 1, ncells + write(iunit, '(2F12.6)') xcc(i), u_weno(i) + end do + close(iunit) + + ! Write analytical results + open(newunit=iunit, file='analytical_results.txt', status='replace') + write(iunit, '(A)') '# x, u (Analytical)' + do i = 1, ncells + write(iunit, '(2F12.6)') xcc(i), u_analytical(i) + end do + close(iunit) + + print *, "==========================================" + print *, "Simulation completed successfully!" + print *, "Results written to:" + print *, " eno_results.txt" + print *, " weno_results.txt" + print *, " analytical_results.txt" + print *, "" + print *, "To generate the comparison plot, run:" + print *, " python postprocess.py" + print *, "==========================================" + + deallocate(u_eno, u_weno, u_analytical, xcc) + end subroutine performEnoWenoAnalysis + +end module cfd_solver + +! =================================================================== +! Main Program +! =================================================================== +program main + use cfd_solver + implicit none + + print *, "==========================================" + print *, "OneFLOW-CFD Solver for 1D Convection" + print *, "ENO3 vs WENO3 Comparison" + print *, "==========================================" + + call performEnoWenoAnalysis() + + print *, "Program finished successfully!" + +end program main \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01a/postprocess.py b/example/1d-linear-convection/weno3/fortran/cfd/01a/postprocess.py new file mode 100644 index 00000000..6cbcc4d9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01a/postprocess.py @@ -0,0 +1,38 @@ +import numpy as np +import matplotlib.pyplot as plt + +def read_results(filename): + """Read results from Fortran output file""" + data = np.loadtxt(filename, comments='#') + return data[:, 0], data[:, 1] + +def main(): + # Read results + x_eno, u_eno = read_results('eno_results.txt') + x_weno, u_weno = read_results('weno_results.txt') + x_analytical, u_analytical = read_results('analytical_results.txt') + + # Create plot + plt.figure(figsize=(12, 8)) + plt.plot(x_eno, u_eno, 'bo-', linewidth=1.5, markersize=4, + markerfacecolor='none', label='ENO3 (Rusanov)') + plt.plot(x_weno, u_weno, 'gs-', linewidth=1.5, markersize=4, + markerfacecolor='none', label='WENO3 (Rusanov)') + plt.plot(x_analytical, u_analytical, 'r--', linewidth=2, label='Analytical') + + plt.title('1D Convection Equation: ENO3 vs WENO3 Comparison (t=0.625)', fontsize=14) + plt.xlabel('x', fontsize=12) + plt.ylabel('u', fontsize=12) + plt.legend(fontsize=12) + plt.grid(True, alpha=0.3) + plt.tight_layout() + + # Save figure + plt.savefig('eno_weno_comparison.png', dpi=300, bbox_inches='tight') + print("Plot saved as: eno_weno_comparison.png") + + # Show plot + plt.show() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01b/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/cfd/01b/CMakeLists.txt new file mode 100644 index 00000000..653a6751 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01b/CMakeLists.txt @@ -0,0 +1,22 @@ +# CMakeLists.txt +cmake_minimum_required(VERSION 4.2.1) # 更高版本更好支持Fortran + +project(OneFLOW_CFD LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +# 按依赖顺序显式列出源文件(解决 Intel + VS 编译顺序问题) +set(SOURCES + cfd_solver.f90 +) + +add_executable(oneflow_cfd ${SOURCES}) + +# 包含模块目录(解决 .mod 未找到) +target_include_directories(oneflow_cfd PRIVATE ${CMAKE_Fortran_MODULE_DIRECTORY}) diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01b/cfd_solver.f90 b/example/1d-linear-convection/weno3/fortran/cfd/01b/cfd_solver.f90 new file mode 100644 index 00000000..0bfb7747 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01b/cfd_solver.f90 @@ -0,0 +1,861 @@ +! OneFLOW-CFD Solver for 1D Convection Equation +! ENO/WENO Reconstruction Comparison - Single File Implementation + +module cfd_solver + use, intrinsic :: iso_fortran_env, only: dp => real64 + implicit none + + private + + ! =================================================================== + ! Forward Type Declarations + ! =================================================================== + type, public :: CfdConfigType + character(len=10) :: recon_scheme = "eno" + integer :: flux_type = 0 + integer :: rk_order = 1 + integer :: spatial_order = 3 + real(dp) :: wave_speed = 1.0_dp + real(dp) :: final_time = 0.625_dp + real(dp) :: dt = 0.025_dp + end type CfdConfigType + + type, public :: MeshType + real(dp) :: xmin = 0.0_dp + real(dp) :: xmax = 2.0_dp + integer :: ncells = 40 + integer :: nnodes = 0 + integer :: nx = 0 + real(dp) :: L = 0.0_dp + real(dp) :: dx = 0.0_dp + real(dp), allocatable :: x(:), xcc(:) + contains + procedure :: init => mesh_init + end type MeshType + + type, public :: ComputationalDomainType + type(MeshType) :: mesh + type(CfdConfigType) :: config + integer :: nghosts = 0 + integer :: ist = 0 + integer :: ied = 0 + integer :: ntcells = 0 + contains + procedure :: init => domain_init + end type ComputationalDomainType + + type, public :: SolutionType + type(ComputationalDomainType) :: domain + real(dp), allocatable :: q_face_left(:), q_face_right(:) + real(dp), allocatable :: flux(:), res(:) + real(dp), allocatable :: u(:), un(:) + contains + procedure :: init => solution_init + end type SolutionType + + ! Main CFD solver class - defined BEFORE abstract interface + type, public :: CfdType + type(CfdConfigType) :: config + type(ComputationalDomainType) :: domain + type(SolutionType) :: solution + class(*), allocatable :: reconstructor + contains + procedure :: init => cfd_init + end type CfdType + + ! Abstract reconstructor base class + type, abstract, public :: ReconstructorType + contains + procedure(reconstruct_interface), deferred, pass :: reconstruct + end type ReconstructorType + + ! Define abstract interface + abstract interface + subroutine reconstruct_interface(this, q, cfd) + import :: ReconstructorType, CfdType, dp + class(ReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout), target :: cfd + end subroutine reconstruct_interface + end interface + + ! ENO reconstructor + type, extends(ReconstructorType), public :: EnoReconstructorType + integer :: spatial_order + integer :: ntcells + integer, allocatable :: lmc(:) + real(dp), allocatable :: coef(:,:) + real(dp), allocatable :: dd(:,:) + contains + procedure :: reconstruct => eno_reconstruct + procedure :: init => eno_init + end type EnoReconstructorType + + ! WENO reconstructor + type, extends(ReconstructorType), public :: WenoReconstructorType + contains + procedure :: reconstruct => weno_reconstruct + end type WenoReconstructorType + + ! =================================================================== + ! Public Procedures + ! =================================================================== + public :: run_simulation, init_field, analytical_solution, performEnoWenoAnalysis + + ! =================================================================== + ! Module Variables + ! =================================================================== + real(dp), parameter :: eps_weno = 1.0e-6_dp + +contains + + ! =================================================================== + ! Initialization Methods + ! =================================================================== + + ! Mesh initialization + subroutine mesh_init(this) + class(MeshType), intent(inout) :: this + integer :: i + + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, dp) + + allocate(this%x(this%nnodes), this%xcc(this%ncells)) + + ! Node coordinates + do i = 1, this%nnodes + this%x(i) = this%xmin + (i-1) * this%dx + end do + + ! Cell center coordinates + do i = 1, this%ncells + this%xcc(i) = 0.5_dp * (this%x(i) + this%x(i+1)) + end do + end subroutine mesh_init + + ! Domain initialization + subroutine domain_init(this, mesh, config) + class(ComputationalDomainType), intent(inout) :: this + type(MeshType), intent(in) :: mesh + type(CfdConfigType), intent(in) :: config + + this%mesh = mesh + this%config = config + + ! Calculate ghost cells + if (trim(config%recon_scheme) == "eno") then + this%nghosts = config%spatial_order + else if (trim(config%recon_scheme) == "weno") then + this%nghosts = config%spatial_order / 2 + 1 + else + error stop "Unknown reconstruction scheme" + end if + + this%ist = this%nghosts + 1 + this%ied = this%ist + mesh%ncells - 1 + this%ntcells = mesh%ncells + 2 * this%nghosts + + print *, "Domain initialized:" + print *, " mesh.ncells = ", mesh%ncells + print *, " spatial_order = ", config%spatial_order + print *, " nghosts = ", this%nghosts + print *, " ist = ", this%ist, ", ied = ", this%ied + end subroutine domain_init + + ! Solution initialization + subroutine solution_init(this, domain) + class(SolutionType), intent(inout) :: this + type(ComputationalDomainType), intent(in) :: domain + + this%domain = domain + + allocate(this%q_face_left(domain%mesh%nnodes)) + allocate(this%q_face_right(domain%mesh%nnodes)) + allocate(this%flux(domain%mesh%nnodes)) + allocate(this%res(domain%mesh%ncells)) + allocate(this%u(domain%ntcells)) + allocate(this%un(domain%ntcells)) + + this%q_face_left = 0.0_dp + this%q_face_right = 0.0_dp + this%flux = 0.0_dp + this%res = 0.0_dp + this%u = 0.0_dp + this%un = 0.0_dp + end subroutine solution_init + + ! ENO reconstructor initialization + subroutine eno_init(this, spatial_order, ntcells) + class(EnoReconstructorType), intent(inout) :: this + integer, intent(in) :: spatial_order + integer, intent(in) :: ntcells + + this%spatial_order = spatial_order + this%ntcells = ntcells + + allocate(this%lmc(ntcells)) + allocate(this%coef(spatial_order+1, spatial_order)) + allocate(this%dd(spatial_order, ntcells)) + + this%lmc = 0 + this%coef = 0.0_dp + this%dd = 0.0_dp + + ! Initialize coefficients + call init_coef(spatial_order, this%coef) + end subroutine eno_init + + ! CFD solver initialization + subroutine cfd_init(this, config, domain) + class(CfdType), intent(inout) :: this + type(CfdConfigType), intent(in) :: config + type(ComputationalDomainType), intent(in) :: domain + + this%config = config + this%domain = domain + call this%solution%init(domain) + + ! Create reconstructor based on scheme + if (trim(config%recon_scheme) == "eno") then + allocate(EnoReconstructorType :: this%reconstructor) + select type(rec => this%reconstructor) + type is (EnoReconstructorType) + call rec%init(config%spatial_order, domain%ntcells) + end select + else if (trim(config%recon_scheme) == "weno") then + allocate(WenoReconstructorType :: this%reconstructor) + else + error stop "Unknown reconstruction scheme" + end if + end subroutine cfd_init + + ! =================================================================== + ! Initial Conditions and Analytical Solution + ! =================================================================== + + ! Initial condition: step function + function initial_condition(x) result(u0) + real(dp), intent(in) :: x + real(dp) :: u0 + + if (0.5_dp <= x .and. x <= 1.0_dp) then + u0 = 2.0_dp + else + u0 = 1.0_dp + end if + end function initial_condition + + ! Analytical solution with periodic BC + function analytical_solution(x, t, a, L) result(u) + real(dp), intent(in) :: x, t, a, L + real(dp) :: u, x_shifted + + x_shifted = mod(x - a * t + L, L) + u = initial_condition(x_shifted) + end function analytical_solution + + ! Initialize field with step function + subroutine init_field(cfd) + type(CfdType), intent(inout) :: cfd + integer :: i, j + + do i = cfd%domain%ist, cfd%domain%ied + j = i - cfd%domain%ist + 1 + if (0.5_dp <= cfd%domain%mesh%xcc(j) .and. cfd%domain%mesh%xcc(j) <= 1.0_dp) then + cfd%solution%u(i) = 2.0_dp + else + cfd%solution%u(i) = 1.0_dp + end if + end do + + call boundary(cfd%solution%u, cfd) + call update_oldfield(cfd%solution%un, cfd%solution%u, cfd%domain%ntcells) + end subroutine init_field + + ! =================================================================== + ! Boundary Conditions + ! =================================================================== + + ! Periodic boundary conditions + subroutine periodic_boundary(u, cfd) + real(dp), intent(inout) :: u(:) + type(CfdType), intent(in) :: cfd + integer :: ig + + ! Left ghost cells = right interior cells + do ig = 1, cfd%domain%nghosts + u(cfd%domain%ist - ig) = u(cfd%domain%ied - ig) + end do + + ! Right ghost cells = left interior cells + do ig = 1, cfd%domain%nghosts + u(cfd%domain%ied + ig) = u(cfd%domain%ist + ig - 1) + end do + end subroutine periodic_boundary + + ! Boundary condition wrapper + subroutine boundary(u, cfd) + real(dp), intent(inout) :: u(:) + type(CfdType), intent(in) :: cfd + + call periodic_boundary(u, cfd) + end subroutine boundary + + ! =================================================================== + ! Reconstruction Methods + ! =================================================================== + + ! Initialize ENO/WENO coefficients + subroutine init_coef(spatial_order, coef) + integer, intent(in) :: spatial_order + real(dp), intent(out) :: coef(:,:) + + coef = 0.0_dp + + select case(spatial_order) + case(1) + coef(1,1) = 1.0_dp + coef(2,1) = 1.0_dp + + case(2) + coef(1,1:2) = [ 3.0_dp/2.0_dp, -1.0_dp/2.0_dp ] + coef(2,1:2) = [ 1.0_dp/2.0_dp, 1.0_dp/2.0_dp ] + coef(3,1:2) = [ -1.0_dp/2.0_dp, 3.0_dp/2.0_dp ] + + case(3) + coef(1,1:3) = [ 11.0_dp/6.0_dp, -7.0_dp/6.0_dp, 1.0_dp/3.0_dp ] + coef(2,1:3) = [ 1.0_dp/3.0_dp, 5.0_dp/6.0_dp, -1.0_dp/6.0_dp ] + coef(3,1:3) = [ -1.0_dp/6.0_dp, 5.0_dp/6.0_dp, 1.0_dp/3.0_dp ] + coef(4,1:3) = [ 1.0_dp/3.0_dp, -7.0_dp/6.0_dp, 11.0_dp/6.0_dp ] + + case(4) + coef(1,1:4) = [ 25.0_dp/12.0_dp, -23.0_dp/12.0_dp, 13.0_dp/12.0_dp, -1.0_dp/4.0_dp ] + coef(2,1:4) = [ 1.0_dp/4.0_dp, 13.0_dp/12.0_dp, -5.0_dp/12.0_dp, 1.0_dp/12.0_dp ] + coef(3,1:4) = [ -1.0_dp/12.0_dp, 7.0_dp/12.0_dp, 7.0_dp/12.0_dp, -1.0_dp/12.0_dp ] + coef(4,1:4) = [ 1.0_dp/12.0_dp, -5.0_dp/12.0_dp, 13.0_dp/12.0_dp, 1.0_dp/4.0_dp ] + coef(5,1:4) = [ -1.0_dp/4.0_dp, 13.0_dp/12.0_dp, -23.0_dp/12.0_dp, 25.0_dp/12.0_dp ] + + case default + error stop "Unsupported spatial order" + end select + end subroutine init_coef + + ! ENO reconstruction + subroutine eno_reconstruct(this, q, cfd) + class(EnoReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout), target :: cfd + + integer :: i, j, m, k1, k2, r1, r2 + + ! Compute divided differences + this%dd(1, :) = q + + do m = 2, this%spatial_order + do j = 1, this%ntcells - m + 1 + this%dd(m, j) = this%dd(m-1, j+1) - this%dd(m-1, j) + end do + end do + + ! Select left-biased stencil for each node + do i = cfd%domain%ist-1, cfd%domain%ied + this%lmc(i) = i + do m = 2, this%spatial_order + if (abs(this%dd(m, this%lmc(i)-1)) < abs(this%dd(m, this%lmc(i)))) then + this%lmc(i) = this%lmc(i) - 1 + end if + end do + end do + + ! Reconstruct values at cell interfaces + do i = cfd%domain%ist, cfd%domain%ied + j = i - cfd%domain%ist + 1 + k1 = this%lmc(i-1) + k2 = this%lmc(i) + r1 = i-1 - k1 + r2 = i - k2 + + cfd%solution%q_face_left(j) = 0.0_dp + cfd%solution%q_face_right(j) = 0.0_dp + + do m = 1, this%spatial_order + cfd%solution%q_face_left(j) = cfd%solution%q_face_left(j) + & + q(k1 + m - 1) * this%coef(r1+2, m) + cfd%solution%q_face_right(j) = cfd%solution%q_face_right(j) + & + q(k2 + m - 1) * this%coef(r2+1, m) + end do + end do + end subroutine eno_reconstruct + + ! WENO-3 nonlinear weights for left-biased stencil + function wc3L(v1, v2, v3) result(f) + real(dp), intent(in) :: v1, v2, v3 + real(dp) :: f, s0, s1, d0, d1, c0, c1, w0, w1, q0, q1 + + ! Smoothness indicators + s0 = (v3 - v2)**2 + s1 = (v2 - v1)**2 + + ! Nonlinear weights + d0 = 2.0_dp/3.0_dp + d1 = 1.0_dp/3.0_dp + + c0 = d0 / ((eps_weno + s0)**2) + c1 = d1 / ((eps_weno + s1)**2) + + w0 = c0 / (c0 + c1) + w1 = c1 / (c0 + c1) + + ! Candidate stencils + q0 = 0.5_dp * v2 + 0.5_dp * v3 + q1 = -0.5_dp * v1 + 1.5_dp * v2 + + ! Reconstructed value + f = w0 * q0 + w1 * q1 + end function wc3L + + ! WENO-3 nonlinear weights for right-biased stencil + function wc3R(v1, v2, v3) result(f) + real(dp), intent(in) :: v1, v2, v3 + real(dp) :: f, s0, s1, d0, d1, c0, c1, w0, w1, q0, q1 + + ! Smoothness indicators + s0 = (v2 - v1)**2 + s1 = (v3 - v2)**2 + + ! Nonlinear weights + d0 = 2.0_dp/3.0_dp + d1 = 1.0_dp/3.0_dp + + c0 = d0 / ((eps_weno + s0)**2) + c1 = d1 / ((eps_weno + s1)**2) + + w0 = c0 / (c0 + c1) + w1 = c1 / (c0 + c1) + + ! Candidate stencils + q0 = 0.5_dp * v1 + 0.5_dp * v2 + q1 = 1.5_dp * v2 - 0.5_dp * v3 + + ! Reconstructed value + f = w0 * q0 + w1 * q1 + end function wc3R + + ! WENO-3 reconstruction for left interface + subroutine weno3L_periodic(cfd, u, f) + type(CfdType), intent(in) :: cfd + real(dp), intent(in) :: u(:) + real(dp), intent(out) :: f(:) + + integer :: i, j + + do i = cfd%domain%ist-1, cfd%domain%ied-1 + j = i - (cfd%domain%ist - 1) + f(j) = wc3L(u(i-1), u(i), u(i+1)) + end do + end subroutine weno3L_periodic + + ! WENO-3 reconstruction for right interface + subroutine weno3R_periodic(cfd, u, f) + type(CfdType), intent(in) :: cfd + real(dp), intent(in) :: u(:) + real(dp), intent(out) :: f(:) + + integer :: i, j + + do i = cfd%domain%ist, cfd%domain%ied + j = i - cfd%domain%ist + 1 + f(j) = wc3R(u(i-1), u(i), u(i+1)) + end do + end subroutine weno3R_periodic + + ! WENO reconstruction + subroutine weno_reconstruct(this, q, cfd) + class(WenoReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout), target :: cfd + + call weno3L_periodic(cfd, q, cfd%solution%q_face_left) + call weno3R_periodic(cfd, q, cfd%solution%q_face_right) + end subroutine weno_reconstruct + + ! General reconstruction wrapper + subroutine reconstruction(q, cfd) + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + select type(rec => cfd%reconstructor) + type is (EnoReconstructorType) + call rec%reconstruct(q, cfd) + type is (WenoReconstructorType) + call rec%reconstruct(q, cfd) + class default + error stop "Unknown reconstructor type" + end select + end subroutine reconstruction + + ! =================================================================== + ! Flux Functions + ! =================================================================== + + ! Rusanov flux + subroutine rusanov_flux(q_face_left, q_face_right, flux, cfd) + real(dp), intent(in) :: q_face_left(:), q_face_right(:) + real(dp), intent(out) :: flux(:) + type(CfdType), intent(in) :: cfd + + integer :: i + real(dp) :: u_L, u_R, F_L, F_R, c_L, c_R, Smax + + c_L = cfd%config%wave_speed + c_R = cfd%config%wave_speed + + do i = 1, cfd%domain%mesh%nnodes + u_L = q_face_left(i) + u_R = q_face_right(i) + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux(i) = 0.5_dp * (F_L + F_R) - 0.5_dp * Smax * (u_R - u_L) + end do + end subroutine rusanov_flux + + ! Engquist-Osher flux + subroutine engquist_osher_flux(q_face_left, q_face_right, flux, cfd) + real(dp), intent(in) :: q_face_left(:), q_face_right(:) + real(dp), intent(out) :: flux(:) + type(CfdType), intent(in) :: cfd + + integer :: i + real(dp) :: c, cp, cm, u_L, u_R + + c = cfd%config%wave_speed + + do i = 1, cfd%domain%mesh%nnodes + cp = 0.5_dp * (c + abs(c)) + cm = 0.5_dp * (c - abs(c)) + u_L = q_face_left(i) + u_R = q_face_right(i) + flux(i) = cp * u_L + cm * u_R + end do + end subroutine engquist_osher_flux + + ! Inviscid flux selection + subroutine inviscid_flux(q_face_left, q_face_right, flux, cfd) + real(dp), intent(in) :: q_face_left(:), q_face_right(:) + real(dp), intent(out) :: flux(:) + type(CfdType), intent(in) :: cfd + + if (cfd%config%flux_type == 0) then + call rusanov_flux(q_face_left, q_face_right, flux, cfd) + else + call engquist_osher_flux(q_face_left, q_face_right, flux, cfd) + end if + end subroutine inviscid_flux + + ! =================================================================== + ! Residual Computation + ! =================================================================== + + ! Compute residual (flux divergence) + subroutine residual(q, cfd) + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + integer :: i + + ! Reconstruction + call reconstruction(q, cfd) + + ! Compute fluxes + call inviscid_flux(cfd%solution%q_face_left, cfd%solution%q_face_right, & + cfd%solution%flux, cfd) + + ! Compute residual + do i = 1, cfd%domain%mesh%ncells + cfd%solution%res(i) = -(cfd%solution%flux(i+1) - cfd%solution%flux(i)) / & + cfd%domain%mesh%dx + end do + end subroutine residual + + ! =================================================================== + ! Time Integration + ! =================================================================== + + ! Update old field + subroutine update_oldfield(qn, q, n) + real(dp), intent(out) :: qn(:) + real(dp), intent(in) :: q(:) + integer, intent(in) :: n + + qn(1:n) = q(1:n) + end subroutine update_oldfield + + ! 1st-order Runge-Kutta (Euler) + subroutine runge_kutta_1(cfd) + type(CfdType), intent(inout) :: cfd + + integer :: i, j + real(dp) :: dt + + dt = cfd%config%dt + + call residual(cfd%solution%u, cfd) + + do i = cfd%domain%ist, cfd%domain%ied + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = cfd%solution%u(i) + dt * cfd%solution%res(j) + end do + + call boundary(cfd%solution%u, cfd) + call update_oldfield(cfd%solution%un, cfd%solution%u, cfd%domain%ntcells) + end subroutine runge_kutta_1 + + ! 2nd-order Runge-Kutta (Heun) + subroutine runge_kutta_2(cfd) + type(CfdType), intent(inout) :: cfd + + integer :: i, j + real(dp) :: dt + + dt = cfd%config%dt + + ! Stage 1 + call residual(cfd%solution%u, cfd) + do i = cfd%domain%ist, cfd%domain%ied + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = cfd%solution%u(i) + dt * cfd%solution%res(j) + end do + call boundary(cfd%solution%u, cfd) + + ! Stage 2 + call residual(cfd%solution%u, cfd) + do i = cfd%domain%ist, cfd%domain%ied + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = 0.5_dp * cfd%solution%un(i) + & + 0.5_dp * cfd%solution%u(i) + & + 0.5_dp * dt * cfd%solution%res(j) + end do + call boundary(cfd%solution%u, cfd) + + call update_oldfield(cfd%solution%un, cfd%solution%u, cfd%domain%ntcells) + end subroutine runge_kutta_2 + + ! 3rd-order Runge-Kutta (SSPRK3) + subroutine runge_kutta_3(cfd) + type(CfdType), intent(inout) :: cfd + + integer :: i, j + real(dp) :: dt + + dt = cfd%config%dt + + ! Stage 1 + call residual(cfd%solution%u, cfd) + do i = cfd%domain%ist, cfd%domain%ied + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = cfd%solution%u(i) + dt * cfd%solution%res(j) + end do + call boundary(cfd%solution%u, cfd) + + ! Stage 2 + call residual(cfd%solution%u, cfd) + do i = cfd%domain%ist, cfd%domain%ied + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = 0.75_dp * cfd%solution%un(i) + & + 0.25_dp * cfd%solution%u(i) + & + 0.25_dp * dt * cfd%solution%res(j) + end do + call boundary(cfd%solution%u, cfd) + + ! Stage 3 + call residual(cfd%solution%u, cfd) + do i = cfd%domain%ist, cfd%domain%ied + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = (1.0_dp/3.0_dp) * cfd%solution%un(i) + & + (2.0_dp/3.0_dp) * cfd%solution%u(i) + & + (2.0_dp/3.0_dp) * dt * cfd%solution%res(j) + end do + call boundary(cfd%solution%u, cfd) + + call update_oldfield(cfd%solution%un, cfd%solution%u, cfd%domain%ntcells) + end subroutine runge_kutta_3 + + ! Runge-Kutta selection + subroutine runge_kutta(cfd) + type(CfdType), intent(inout) :: cfd + + select case(cfd%config%rk_order) + case(1) + call runge_kutta_1(cfd) + case(2) + call runge_kutta_2(cfd) + case(3) + call runge_kutta_3(cfd) + case default + call runge_kutta_1(cfd) + end select + end subroutine runge_kutta + + ! =================================================================== + ! Simulation Driver + ! =================================================================== + + ! Run simulation to final time + function run_simulation(cfd, final_time) result(u_result) + type(CfdType), intent(inout) :: cfd + real(dp), intent(in) :: final_time + real(dp), allocatable :: u_result(:) + + real(dp) :: t, dt, dt_old + + allocate(u_result(cfd%domain%mesh%ncells)) + + t = 0.0_dp + dt_old = cfd%config%dt + dt = dt_old + + do while (t < final_time - 1.0e-12_dp) + if (t + dt > final_time) then + dt = final_time - t + end if + cfd%config%dt = dt + call runge_kutta(cfd) + t = t + dt + end do + + cfd%config%dt = dt_old + + ! Extract physical solution (without ghost cells) + u_result = cfd%solution%u(cfd%domain%ist:cfd%domain%ied) + end function run_simulation + + ! =================================================================== + ! Main Analysis Function + ! =================================================================== + + ! Perform ENO-WENO comparative analysis + subroutine performEnoWenoAnalysis() + type(CfdConfigType) :: config_eno3, config_weno3 + type(MeshType) :: mesh + type(ComputationalDomainType) :: domain_eno3, domain_weno3 + type(CfdType) :: cfd_eno3, cfd_weno3 + real(dp), allocatable :: u_eno(:), u_weno(:), u_analytical(:) + real(dp), allocatable :: xcc(:) + integer :: i, ncells, iunit + + ! Initialize mesh + call mesh%init() + ncells = mesh%ncells + allocate(xcc(ncells)) + xcc = mesh%xcc + + ! Configure ENO3 + config_eno3%recon_scheme = "eno" + config_eno3%spatial_order = 3 + config_eno3%flux_type = 0 + config_eno3%rk_order = 1 + config_eno3%wave_speed = 1.0_dp + config_eno3%final_time = 0.625_dp + config_eno3%dt = 0.0025_dp + + ! Configure WENO3 + config_weno3%recon_scheme = "weno" + config_weno3%spatial_order = 3 + config_weno3%flux_type = 0 + config_weno3%rk_order = 1 + config_weno3%wave_speed = 1.0_dp + config_weno3%final_time = 0.625_dp + config_weno3%dt = 0.0025_dp + + ! Create domains + call domain_eno3%init(mesh, config_eno3) + call domain_weno3%init(mesh, config_weno3) + + ! Create CFD solvers + call cfd_eno3%init(config_eno3, domain_eno3) + call cfd_weno3%init(config_weno3, domain_weno3) + + ! Allocate arrays + allocate(u_eno(ncells), u_weno(ncells), u_analytical(ncells)) + + ! Run ENO simulation + print *, "Running ENO3 simulation..." + call init_field(cfd_eno3) + u_eno = run_simulation(cfd_eno3, config_eno3%final_time) + + ! Run WENO simulation + print *, "Running WENO3 simulation..." + call init_field(cfd_weno3) + u_weno = run_simulation(cfd_weno3, config_weno3%final_time) + + ! Compute analytical solution + print *, "Computing analytical solution..." + do i = 1, ncells + u_analytical(i) = analytical_solution(xcc(i), config_weno3%final_time, & + config_weno3%wave_speed, mesh%L) + end do + + ! Write results to files + print *, "Writing results to files..." + + ! Write ENO results + open(newunit=iunit, file='eno_results.txt', status='replace') + write(iunit, '(A)') '# x, u (ENO3)' + do i = 1, ncells + write(iunit, '(2F12.6)') xcc(i), u_eno(i) + end do + close(iunit) + + ! Write WENO results + open(newunit=iunit, file='weno_results.txt', status='replace') + write(iunit, '(A)') '# x, u (WENO3)' + do i = 1, ncells + write(iunit, '(2F12.6)') xcc(i), u_weno(i) + end do + close(iunit) + + ! Write analytical results + open(newunit=iunit, file='analytical_results.txt', status='replace') + write(iunit, '(A)') '# x, u (Analytical)' + do i = 1, ncells + write(iunit, '(2F12.6)') xcc(i), u_analytical(i) + end do + close(iunit) + + print *, "==========================================" + print *, "Simulation completed successfully!" + print *, "Results written to:" + print *, " eno_results.txt" + print *, " weno_results.txt" + print *, " analytical_results.txt" + print *, "" + print *, "To generate the comparison plot, run:" + print *, " python postprocess.py" + print *, "==========================================" + + deallocate(u_eno, u_weno, u_analytical, xcc) + end subroutine performEnoWenoAnalysis + +end module cfd_solver + +! =================================================================== +! Main Program +! =================================================================== +program main + use cfd_solver + implicit none + + print *, "==========================================" + print *, "OneFLOW-CFD Solver for 1D Convection" + print *, "ENO3 vs WENO3 Comparison" + print *, "==========================================" + + call performEnoWenoAnalysis() + + print *, "Program finished successfully!" + +end program main \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01b/postprocess.py b/example/1d-linear-convection/weno3/fortran/cfd/01b/postprocess.py new file mode 100644 index 00000000..489212dd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01b/postprocess.py @@ -0,0 +1,52 @@ +import numpy as np +import matplotlib.pyplot as plt + +def read_results(filename): + """Read results from Fortran output file""" + try: + data = np.loadtxt(filename, comments='#') + return data[:, 0], data[:, 1] + except Exception as e: + print(f"Error reading {filename}: {e}") + return np.array([]), np.array([]) + +def main(): + print("Reading Fortran output files...") + + # Read all data files + x_eno, u_eno = read_results('eno_results.txt') + x_weno, u_weno = read_results('weno_results.txt') + x_analytical, u_analytical = read_results('analytical_results.txt') + + # Check if we have data + if len(x_eno) == 0 or len(x_weno) == 0 or len(x_analytical) == 0: + print("Error: Could not read all data files.") + print("Make sure to run the Fortran program first.") + return + + # Create plot + plt.figure(figsize=(10, 6)) + + # Plot results + plt.plot(x_eno, u_eno, 'bo-', linewidth=1, markersize=3, + markerfacecolor='none', label='ENO3') + plt.plot(x_weno, u_weno, 'gs-', linewidth=1, markersize=3, + markerfacecolor='none', label='WENO3') + plt.plot(x_analytical, u_analytical, 'r-', linewidth=2, label='Analytical') + + # Customize plot + plt.title('1D Convection: ENO3 vs WENO3 (t=0.625)') + plt.xlabel('x') + plt.ylabel('u') + plt.legend() + plt.grid(True, alpha=0.3) + + # Save and show + plt.tight_layout() + plt.savefig('comparison.png', dpi=150) + plt.show() + + print("Plot saved as comparison.png") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01c/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/cfd/01c/CMakeLists.txt new file mode 100644 index 00000000..653a6751 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01c/CMakeLists.txt @@ -0,0 +1,22 @@ +# CMakeLists.txt +cmake_minimum_required(VERSION 4.2.1) # 更高版本更好支持Fortran + +project(OneFLOW_CFD LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +# 按依赖顺序显式列出源文件(解决 Intel + VS 编译顺序问题) +set(SOURCES + cfd_solver.f90 +) + +add_executable(oneflow_cfd ${SOURCES}) + +# 包含模块目录(解决 .mod 未找到) +target_include_directories(oneflow_cfd PRIVATE ${CMAKE_Fortran_MODULE_DIRECTORY}) diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01c/cfd_solver.f90 b/example/1d-linear-convection/weno3/fortran/cfd/01c/cfd_solver.f90 new file mode 100644 index 00000000..b0d385d2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01c/cfd_solver.f90 @@ -0,0 +1,939 @@ +! OneFLOW-CFD Solver for 1D Convection Equation +! ENO/WENO Reconstruction Comparison - Single File Implementation + +module cfd_solver + use, intrinsic :: iso_fortran_env, only: dp => real64 + implicit none + + private + + ! =================================================================== + ! Forward Type Declarations + ! =================================================================== + type, public :: CfdConfigType + character(len=10) :: recon_scheme = "eno" + integer :: flux_type = 0 + integer :: rk_order = 1 + integer :: spatial_order = 3 + real(dp) :: wave_speed = 1.0_dp + real(dp) :: final_time = 0.625_dp + real(dp) :: dt = 0.001_dp ! 减小时间步长 + real(dp) :: cfl = 0.5_dp ! CFL数 + end type CfdConfigType + + type, public :: MeshType + real(dp) :: xmin = 0.0_dp + real(dp) :: xmax = 2.0_dp + integer :: ncells = 40 + integer :: nnodes = 0 + integer :: nx = 0 + real(dp) :: L = 0.0_dp + real(dp) :: dx = 0.0_dp + real(dp), allocatable :: x(:), xcc(:) + contains + procedure :: init => mesh_init + end type MeshType + + type, public :: ComputationalDomainType + type(MeshType) :: mesh + type(CfdConfigType) :: config + integer :: nghosts = 0 + integer :: ist = 0 + integer :: ied = 0 + integer :: ntcells = 0 + contains + procedure :: init => domain_init + end type ComputationalDomainType + + type, public :: SolutionType + type(ComputationalDomainType) :: domain + real(dp), allocatable :: q_face_left(:), q_face_right(:) + real(dp), allocatable :: flux(:), res(:) + real(dp), allocatable :: u(:), un(:) + contains + procedure :: init => solution_init + end type SolutionType + + ! Main CFD solver class + type, public :: CfdType + type(CfdConfigType) :: config + type(ComputationalDomainType) :: domain + type(SolutionType) :: solution + class(*), allocatable :: reconstructor + contains + procedure :: init => cfd_init + end type CfdType + + ! Abstract reconstructor base class + type, abstract, public :: ReconstructorType + contains + procedure(reconstruct_interface), deferred, pass :: reconstruct + end type ReconstructorType + + ! Define abstract interface + abstract interface + subroutine reconstruct_interface(this, q, cfd) + import :: ReconstructorType, CfdType, dp + class(ReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + end subroutine reconstruct_interface + end interface + + ! ENO reconstructor + type, extends(ReconstructorType), public :: EnoReconstructorType + integer :: spatial_order + integer :: ntcells + integer, allocatable :: lmc(:) + real(dp), allocatable :: coef(:,:) + real(dp), allocatable :: dd(:,:) + contains + procedure :: reconstruct => eno_reconstruct + procedure :: init => eno_init + end type EnoReconstructorType + + ! WENO reconstructor + type, extends(ReconstructorType), public :: WenoReconstructorType + contains + procedure :: reconstruct => weno_reconstruct + end type WenoReconstructorType + + ! =================================================================== + ! Public Procedures + ! =================================================================== + public :: run_simulation, init_field, analytical_solution, performEnoWenoAnalysis + + ! =================================================================== + ! Module Variables + ! =================================================================== + real(dp), parameter :: eps_weno = 1.0e-6_dp + +contains + + ! =================================================================== + ! Initialization Methods + ! =================================================================== + + ! Mesh initialization + subroutine mesh_init(this) + class(MeshType), intent(inout) :: this + integer :: i + + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, dp) + + allocate(this%x(this%nnodes), this%xcc(this%ncells)) + + ! Node coordinates + do i = 1, this%nnodes + this%x(i) = this%xmin + (i-1) * this%dx + end do + + ! Cell center coordinates + do i = 1, this%ncells + this%xcc(i) = 0.5_dp * (this%x(i) + this%x(i+1)) + end do + end subroutine mesh_init + + ! Domain initialization + subroutine domain_init(this, mesh, config) + class(ComputationalDomainType), intent(inout) :: this + type(MeshType), intent(in) :: mesh + type(CfdConfigType), intent(in) :: config + + this%mesh = mesh + this%config = config + + ! Calculate ghost cells + if (trim(config%recon_scheme) == "eno") then + this%nghosts = config%spatial_order + else if (trim(config%recon_scheme) == "weno") then + this%nghosts = config%spatial_order / 2 + 1 + else + error stop "Unknown reconstruction scheme" + end if + + this%ist = this%nghosts + 1 + this%ied = this%ist + mesh%ncells - 1 + this%ntcells = mesh%ncells + 2 * this%nghosts + + print *, "Domain initialized:" + print *, " mesh.ncells = ", mesh%ncells + print *, " spatial_order = ", config%spatial_order + print *, " nghosts = ", this%nghosts + print *, " ist = ", this%ist, ", ied = ", this%ied + print *, " dx = ", mesh%dx + end subroutine domain_init + + ! Solution initialization + subroutine solution_init(this, domain) + class(SolutionType), intent(inout) :: this + type(ComputationalDomainType), intent(in) :: domain + + this%domain = domain + + allocate(this%q_face_left(domain%mesh%nnodes)) + allocate(this%q_face_right(domain%mesh%nnodes)) + allocate(this%flux(domain%mesh%nnodes)) + allocate(this%res(domain%mesh%ncells)) + allocate(this%u(domain%ntcells)) + allocate(this%un(domain%ntcells)) + + this%q_face_left = 0.0_dp + this%q_face_right = 0.0_dp + this%flux = 0.0_dp + this%res = 0.0_dp + this%u = 0.0_dp + this%un = 0.0_dp + end subroutine solution_init + + ! ENO reconstructor initialization + subroutine eno_init(this, spatial_order, ntcells) + class(EnoReconstructorType), intent(inout) :: this + integer, intent(in) :: spatial_order + integer, intent(in) :: ntcells + + this%spatial_order = spatial_order + this%ntcells = ntcells + + allocate(this%lmc(ntcells)) + allocate(this%coef(spatial_order+1, spatial_order)) + allocate(this%dd(spatial_order, ntcells)) + + this%lmc = 0 + this%coef = 0.0_dp + this%dd = 0.0_dp + + ! Initialize coefficients + call init_coef(spatial_order, this%coef) + end subroutine eno_init + + ! CFD solver initialization + subroutine cfd_init(this, config, domain) + class(CfdType), intent(inout) :: this + type(CfdConfigType), intent(in) :: config + type(ComputationalDomainType), intent(in) :: domain + + this%config = config + this%domain = domain + call this%solution%init(domain) + + ! Create reconstructor based on scheme + if (trim(config%recon_scheme) == "eno") then + allocate(EnoReconstructorType :: this%reconstructor) + select type(rec => this%reconstructor) + type is (EnoReconstructorType) + call rec%init(config%spatial_order, domain%ntcells) + end select + else if (trim(config%recon_scheme) == "weno") then + allocate(WenoReconstructorType :: this%reconstructor) + else + error stop "Unknown reconstruction scheme" + end if + + ! Adjust time step based on CFL condition + call calculate_dt(this) + end subroutine cfd_init + + ! Calculate time step based on CFL condition + subroutine calculate_dt(cfd) + type(CfdType), intent(inout) :: cfd + + real(dp) :: dt_cfl + + ! CFL condition: dt <= CFL * dx / |wave_speed| + dt_cfl = cfd%config%cfl * cfd%domain%mesh%dx / abs(cfd%config%wave_speed) + + if (cfd%config%dt > dt_cfl) then + print *, "Adjusting time step for stability:" + print *, " Original dt = ", cfd%config%dt + print *, " CFL dt = ", dt_cfl + cfd%config%dt = dt_cfl + print *, " Using dt = ", cfd%config%dt + end if + end subroutine calculate_dt + + ! =================================================================== + ! Initial Conditions and Analytical Solution + ! =================================================================== + + ! Initial condition: step function + function initial_condition(x) result(u0) + real(dp), intent(in) :: x + real(dp) :: u0 + + if (0.5_dp <= x .and. x <= 1.0_dp) then + u0 = 2.0_dp + else + u0 = 1.0_dp + end if + end function initial_condition + + ! Analytical solution with periodic BC + function analytical_solution(x, t, a, L) result(u) + real(dp), intent(in) :: x, t, a, L + real(dp) :: u, x_shifted + + x_shifted = mod(x - a * t + L, L) + u = initial_condition(x_shifted) + end function analytical_solution + + ! Initialize field with step function + subroutine init_field(cfd) + type(CfdType), intent(inout) :: cfd + integer :: i, j + + do i = 1, cfd%domain%mesh%ncells + if (0.5_dp <= cfd%domain%mesh%xcc(i) .and. cfd%domain%mesh%xcc(i) <= 1.0_dp) then + cfd%solution%u(cfd%domain%ist + i - 1) = 2.0_dp + else + cfd%solution%u(cfd%domain%ist + i - 1) = 1.0_dp + end if + end do + + call boundary(cfd%solution%u, cfd) + call update_oldfield(cfd%solution%un, cfd%solution%u, cfd%domain%ntcells) + end subroutine init_field + + ! =================================================================== + ! Boundary Conditions + ! =================================================================== + + ! Periodic boundary conditions - CORRECTED VERSION + subroutine periodic_boundary(u, cfd) + real(dp), intent(inout) :: u(:) + type(CfdType), intent(in) :: cfd + integer :: i + + ! Copy interior cells to ghost cells + ! Left ghost cells = right interior cells + do i = 1, cfd%domain%nghosts + u(cfd%domain%ist - i) = u(cfd%domain%ied - cfd%domain%nghosts + i - 1) + end do + + ! Right ghost cells = left interior cells + do i = 1, cfd%domain%nghosts + u(cfd%domain%ied + i) = u(cfd%domain%ist + i - 1) + end do + end subroutine periodic_boundary + + ! Boundary condition wrapper + subroutine boundary(u, cfd) + real(dp), intent(inout) :: u(:) + type(CfdType), intent(in) :: cfd + + call periodic_boundary(u, cfd) + end subroutine boundary + + ! =================================================================== + ! Reconstruction Methods + ! =================================================================== + + ! Initialize ENO/WENO coefficients + subroutine init_coef(spatial_order, coef) + integer, intent(in) :: spatial_order + real(dp), intent(out) :: coef(:,:) + + coef = 0.0_dp + + select case(spatial_order) + case(1) + coef(1,1) = 1.0_dp + coef(2,1) = 1.0_dp + + case(2) + coef(1,1:2) = [ 3.0_dp/2.0_dp, -1.0_dp/2.0_dp ] + coef(2,1:2) = [ 1.0_dp/2.0_dp, 1.0_dp/2.0_dp ] + coef(3,1:2) = [ -1.0_dp/2.0_dp, 3.0_dp/2.0_dp ] + + case(3) + coef(1,1:3) = [ 11.0_dp/6.0_dp, -7.0_dp/6.0_dp, 1.0_dp/3.0_dp ] + coef(2,1:3) = [ 1.0_dp/3.0_dp, 5.0_dp/6.0_dp, -1.0_dp/6.0_dp ] + coef(3,1:3) = [ -1.0_dp/6.0_dp, 5.0_dp/6.0_dp, 1.0_dp/3.0_dp ] + coef(4,1:3) = [ 1.0_dp/3.0_dp, -7.0_dp/6.0_dp, 11.0_dp/6.0_dp ] + + case default + error stop "Unsupported spatial order" + end select + end subroutine init_coef + + ! ENO reconstruction - SIMPLIFIED VERSION + subroutine first_order_reconstruct(this, q, cfd) + class(EnoReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + integer :: i, j + integer :: nghosts, ist, ied + + nghosts = cfd%domain%nghosts + ist = cfd%domain%ist + ied = cfd%domain%ied + + ! For now, use simple 2nd order reconstruction + do i = ist, ied + j = i - ist + 1 ! 1-based index for interfaces + + ! Simple averaging for testing + !cfd%solution%q_face_left(j) = 0.5_dp * (q(i-1) + q(i)) + !cfd%solution%q_face_right(j) = 0.5_dp * (q(i) + q(i+1)) + ! Simple averaging for testing + cfd%solution%q_face_left(j) = q(i-1) + cfd%solution%q_face_right(j) = q(i) + end do + end subroutine first_order_reconstruct + + subroutine eno_reconstruct(this, q, cfd) + class(EnoReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + type(ComputationalDomainType), pointer :: domain + + integer :: i, j, m, k1, k2, r1, r2 + integer :: nghosts, ist, ied + + nghosts = cfd%domain%nghosts + ist = cfd%domain%ist + ied = cfd%domain%ied + + ! 1. 差商计算 (dd[1,:] = q) + this%dd(1, :) = q(:) + do m = 2, this%spatial_order + do j = 1, cfd%domain%ntcells - m + 1 + this%dd(m, j) = this%dd(m-1, j+1) - this%dd(m-1, j) + end do + end do + + ! 2. 选择 smoothest stencil + do i = ist - 1, ied ! Python: range(ist-1, ied+1) → ied+1-1 = ied + this%lmc(i) = i + do m = 2, this%spatial_order + if ( abs(this%dd(m, this%lmc(i) - 1) ) < abs(this%dd(m, this%lmc(i)))) then + this%lmc(i) = this%lmc(i) - 1 + end if + end do + end do + + associate ( & + q_face_left => cfd%solution%q_face_left, & + q_face_right => cfd%solution%q_face_right & + ) + ! 这里可以直接使用 q_face_left 和 q_face_right + ! 3. 重构界面值 + do i = ist, ied + j = i - ist + 1 ! 1-based index for interfaces + k1 = this%lmc(i - 1) + k2 = this%lmc(i) + r1 = (i - 1) - k1 + 1 + r2 = i - k2 + 1 + q_face_left(j) = 0.0 + q_face_right(j) = 0.0 + do m = 1, this%spatial_order + q_face_left(j) = q_face_left(j) + q(k1 + m - 1) * this%coef(r1 + 1, m) + q_face_right(j) = q_face_right(j) + q(k2 + m - 1) * this%coef(r2, m) + end do + end do + end associate + + end subroutine eno_reconstruct + + ! WENO-3 nonlinear weights for left-biased stencil + function wc3L(v1, v2, v3) result(f) + real(dp), intent(in) :: v1, v2, v3 + real(dp) :: f, s0, s1, d0, d1, c0, c1, w0, w1, q0, q1 + + ! Smoothness indicators + s0 = (v3 - v2)**2 + s1 = (v2 - v1)**2 + + ! Nonlinear weights + d0 = 2.0_dp/3.0_dp + d1 = 1.0_dp/3.0_dp + + c0 = d0 / ((eps_weno + s0)**2) + c1 = d1 / ((eps_weno + s1)**2) + + w0 = c0 / (c0 + c1) + w1 = c1 / (c0 + c1) + + ! Candidate stencils + q0 = 0.5_dp * v2 + 0.5_dp * v3 + q1 = -0.5_dp * v1 + 1.5_dp * v2 + + ! Reconstructed value + f = w0 * q0 + w1 * q1 + end function wc3L + + ! WENO-3 nonlinear weights for right-biased stencil + function wc3R(v1, v2, v3) result(f) + real(dp), intent(in) :: v1, v2, v3 + real(dp) :: f, s0, s1, d0, d1, c0, c1, w0, w1, q0, q1 + + ! Smoothness indicators + s0 = (v2 - v1)**2 + s1 = (v3 - v2)**2 + + ! Nonlinear weights + d0 = 2.0_dp/3.0_dp + d1 = 1.0_dp/3.0_dp + + c0 = d0 / ((eps_weno + s0)**2) + c1 = d1 / ((eps_weno + s1)**2) + + w0 = c0 / (c0 + c1) + w1 = c1 / (c0 + c1) + + ! Candidate stencils + q0 = 0.5_dp * v1 + 0.5_dp * v2 + q1 = 1.5_dp * v2 - 0.5_dp * v3 + + ! Reconstructed value + f = w0 * q0 + w1 * q1 + end function wc3R + + ! WENO-3 reconstruction for left interface + subroutine weno3L_periodic(cfd, u, f) + type(CfdType), intent(in) :: cfd + real(dp), intent(in) :: u(:) + real(dp), intent(out) :: f(:) + + integer :: i, j, nghosts, ist, ied + + nghosts = cfd%domain%nghosts + ist = cfd%domain%ist + ied = cfd%domain%ied + + do i = ist-1, ied-1 + j = i - (ist - 1) + f(j) = wc3L(u(i-1), u(i), u(i+1)) + end do + end subroutine weno3L_periodic + + ! WENO-3 reconstruction for right interface + subroutine weno3R_periodic(cfd, u, f) + type(CfdType), intent(in) :: cfd + real(dp), intent(in) :: u(:) + real(dp), intent(out) :: f(:) + + integer :: i, j, nghosts, ist, ied + + nghosts = cfd%domain%nghosts + ist = cfd%domain%ist + ied = cfd%domain%ied + + do i = ist, ied + j = i - ist + 1 + f(j) = wc3R(u(i-1), u(i), u(i+1)) + end do + end subroutine weno3R_periodic + + ! WENO reconstruction + subroutine weno_reconstruct(this, q, cfd) + class(WenoReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + call weno3L_periodic(cfd, q, cfd%solution%q_face_left) + call weno3R_periodic(cfd, q, cfd%solution%q_face_right) + end subroutine weno_reconstruct + + ! General reconstruction wrapper + subroutine reconstruction(q, cfd) + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + select type(rec => cfd%reconstructor) + type is (EnoReconstructorType) + call rec%reconstruct(q, cfd) + type is (WenoReconstructorType) + call rec%reconstruct(q, cfd) + class default + error stop "Unknown reconstructor type" + end select + end subroutine reconstruction + + ! =================================================================== + ! Flux Functions + ! =================================================================== + + ! Rusanov flux + subroutine rusanov_flux(q_face_left, q_face_right, flux, cfd) + real(dp), intent(in) :: q_face_left(:), q_face_right(:) + real(dp), intent(out) :: flux(:) + type(CfdType), intent(in) :: cfd + + integer :: i + real(dp) :: u_L, u_R, F_L, F_R, c_L, c_R, Smax + + c_L = cfd%config%wave_speed + c_R = cfd%config%wave_speed + + do i = 1, cfd%domain%mesh%nnodes + u_L = q_face_left(i) + u_R = q_face_right(i) + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux(i) = 0.5_dp * (F_L + F_R) - 0.5_dp * Smax * (u_R - u_L) + end do + end subroutine rusanov_flux + + ! Engquist-Osher flux + subroutine engquist_osher_flux(q_face_left, q_face_right, flux, cfd) + real(dp), intent(in) :: q_face_left(:), q_face_right(:) + real(dp), intent(out) :: flux(:) + type(CfdType), intent(in) :: cfd + + integer :: i + real(dp) :: c, cp, cm, u_L, u_R + + c = cfd%config%wave_speed + + do i = 1, cfd%domain%mesh%nnodes + cp = 0.5_dp * (c + abs(c)) + cm = 0.5_dp * (c - abs(c)) + u_L = q_face_left(i) + u_R = q_face_right(i) + flux(i) = cp * u_L + cm * u_R + end do + end subroutine engquist_osher_flux + + ! Inviscid flux selection + subroutine inviscid_flux(q_face_left, q_face_right, flux, cfd) + real(dp), intent(in) :: q_face_left(:), q_face_right(:) + real(dp), intent(out) :: flux(:) + type(CfdType), intent(in) :: cfd + + if (cfd%config%flux_type == 0) then + call rusanov_flux(q_face_left, q_face_right, flux, cfd) + else + call engquist_osher_flux(q_face_left, q_face_right, flux, cfd) + end if + end subroutine inviscid_flux + + ! =================================================================== + ! Residual Computation + ! =================================================================== + + ! Compute residual (flux divergence) - CORRECTED VERSION + subroutine residual(q, cfd) + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + integer :: i + + ! Apply boundary conditions first + call boundary(cfd%solution%u, cfd) + + ! Reconstruction + call reconstruction(q, cfd) + + ! Compute fluxes + call inviscid_flux(cfd%solution%q_face_left, cfd%solution%q_face_right, & + cfd%solution%flux, cfd) + + ! Compute residual - corrected indexing + do i = 1, cfd%domain%mesh%ncells + cfd%solution%res(i) = -(cfd%solution%flux(i+1) - cfd%solution%flux(i)) / & + cfd%domain%mesh%dx + end do + end subroutine residual + + ! =================================================================== + ! Time Integration + ! =================================================================== + + ! Update old field + subroutine update_oldfield(qn, q, n) + real(dp), intent(out) :: qn(:) + real(dp), intent(in) :: q(:) + integer, intent(in) :: n + + qn(1:n) = q(1:n) + end subroutine update_oldfield + + ! 1st-order Runge-Kutta (Euler) - SIMPLIFIED + subroutine runge_kutta_1(cfd) + type(CfdType), intent(inout) :: cfd + + integer :: i, j + real(dp) :: dt + + dt = cfd%config%dt + + ! Apply boundary conditions + call boundary(cfd%solution%u, cfd) + + ! Compute residual + call residual(cfd%solution%u, cfd) + + ! Update solution + do i = cfd%domain%ist, cfd%domain%ied - 1 + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = cfd%solution%u(i) + dt * cfd%solution%res(j) + end do + + ! Apply boundary conditions again + call boundary(cfd%solution%u, cfd) + + ! Save old solution + call update_oldfield(cfd%solution%un, cfd%solution%u, cfd%domain%ntcells) + end subroutine runge_kutta_1 + + ! 2nd-order Runge-Kutta (Heun) + subroutine runge_kutta_2(cfd) + type(CfdType), intent(inout) :: cfd + + integer :: i, j + real(dp) :: dt + + dt = cfd%config%dt + + ! Stage 1 + call boundary(cfd%solution%u, cfd) + call residual(cfd%solution%u, cfd) + + do i = cfd%domain%ist, cfd%domain%ied - 1 + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = cfd%solution%u(i) + dt * cfd%solution%res(j) + end do + call boundary(cfd%solution%u, cfd) + + ! Stage 2 + call residual(cfd%solution%u, cfd) + do i = cfd%domain%ist, cfd%domain%ied - 1 + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = 0.5_dp * cfd%solution%un(i) + & + 0.5_dp * cfd%solution%u(i) + & + 0.5_dp * dt * cfd%solution%res(j) + end do + call boundary(cfd%solution%u, cfd) + + call update_oldfield(cfd%solution%un, cfd%solution%u, cfd%domain%ntcells) + end subroutine runge_kutta_2 + + ! Runge-Kutta selection + subroutine runge_kutta(cfd) + type(CfdType), intent(inout) :: cfd + + select case(cfd%config%rk_order) + case(1) + call runge_kutta_1(cfd) + case(2) + call runge_kutta_2(cfd) + case default + call runge_kutta_1(cfd) + end select + end subroutine runge_kutta + + ! =================================================================== + ! Simulation Driver + ! =================================================================== + + ! Run simulation to final time + function run_simulation(cfd, final_time) result(u_result) + type(CfdType), intent(inout) :: cfd + real(dp), intent(in) :: final_time + real(dp), allocatable :: u_result(:) + + real(dp) :: t, dt, dt_old + integer :: step, max_steps + + allocate(u_result(cfd%domain%mesh%ncells)) + + t = 0.0_dp + dt_old = cfd%config%dt + dt = dt_old + max_steps = 10000 ! Safety limit + + print *, "Starting time integration..." + print *, " Final time: ", final_time + print *, " Time step: ", dt + print *, " CFL number: ", cfd%config%cfl + + step = 0 + do while (t < final_time - 1.0e-12_dp .and. step < max_steps) + step = step + 1 + + if (t + dt > final_time) then + dt = final_time - t + end if + + cfd%config%dt = dt + call runge_kutta(cfd) + t = t + dt + + ! Progress report + if (mod(step, 100) == 0) then + print *, " Step ", step, ", Time = ", t + end if + end do + + if (step >= max_steps) then + print *, "Warning: Reached maximum number of steps (", max_steps, ")" + end if + + cfd%config%dt = dt_old + + print *, "Time integration completed:" + print *, " Total steps: ", step + print *, " Final time: ", t + + ! Extract physical solution (without ghost cells) + u_result = cfd%solution%u(cfd%domain%ist:cfd%domain%ied) + end function run_simulation + + ! =================================================================== + ! Main Analysis Function + ! =================================================================== + + ! Perform ENO-WENO comparative analysis + subroutine performEnoWenoAnalysis() + type(CfdConfigType) :: config_eno3, config_weno3 + type(MeshType) :: mesh + type(ComputationalDomainType) :: domain_eno3, domain_weno3 + type(CfdType) :: cfd_eno3, cfd_weno3 + real(dp), allocatable :: u_eno(:), u_weno(:), u_analytical(:) + real(dp), allocatable :: xcc(:) + integer :: i, ncells, iunit + + ! Initialize mesh + call mesh%init() + ncells = mesh%ncells + allocate(xcc(ncells)) + xcc = mesh%xcc + + print *, "==========================================" + print *, "Mesh parameters:" + print *, " ncells = ", ncells + print *, " dx = ", mesh%dx + print *, " L = ", mesh%L + print *, "==========================================" + + ! Configure ENO3 - using simple 2nd order for stability + config_eno3%recon_scheme = "eno" + config_eno3%spatial_order = 2 ! Use 2nd order for stability + config_eno3%flux_type = 0 + config_eno3%rk_order = 1 + config_eno3%wave_speed = 1.0_dp + config_eno3%final_time = 0.625_dp + config_eno3%cfl = 0.5_dp + config_eno3%dt = 0.001_dp ! Small time step + + ! Configure WENO3 + config_weno3%recon_scheme = "weno" + config_weno3%spatial_order = 3 + config_weno3%flux_type = 0 + config_weno3%rk_order = 1 + config_weno3%wave_speed = 1.0_dp + config_weno3%final_time = 0.625_dp + config_weno3%cfl = 0.3_dp ! More strict CFL for WENO + config_weno3%dt = 0.0005_dp ! Even smaller time step + + ! Create domains + call domain_eno3%init(mesh, config_eno3) + call domain_weno3%init(mesh, config_weno3) + + ! Create CFD solvers + call cfd_eno3%init(config_eno3, domain_eno3) + call cfd_weno3%init(config_weno3, domain_weno3) + + ! Allocate arrays + allocate(u_eno(ncells), u_weno(ncells), u_analytical(ncells)) + + ! Run ENO simulation + print *, "==========================================" + print *, "Running ENO simulation..." + print *, " Scheme: ENO", config_eno3%spatial_order + print *, " Time step: ", config_eno3%dt + print *, "==========================================" + + call init_field(cfd_eno3) + u_eno = run_simulation(cfd_eno3, config_eno3%final_time) + + ! Run WENO simulation + print *, "==========================================" + print *, "Running WENO simulation..." + print *, " Scheme: WENO", config_weno3%spatial_order + print *, " Time step: ", config_weno3%dt + print *, "==========================================" + + call init_field(cfd_weno3) + u_weno = run_simulation(cfd_weno3, config_weno3%final_time) + + ! Compute analytical solution + print *, "Computing analytical solution..." + do i = 1, ncells + u_analytical(i) = analytical_solution(xcc(i), config_weno3%final_time, & + config_weno3%wave_speed, mesh%L) + end do + + ! Write results to files + print *, "Writing results to files..." + + ! Write ENO results + open(newunit=iunit, file='eno_results.txt', status='replace') + write(iunit, '(A)') '# x, u (ENO)' + do i = 1, ncells + write(iunit, '(2F12.6)') xcc(i), u_eno(i) + end do + close(iunit) + + ! Write WENO results + open(newunit=iunit, file='weno_results.txt', status='replace') + write(iunit, '(A)') '# x, u (WENO)' + do i = 1, ncells + write(iunit, '(2F12.6)') xcc(i), u_weno(i) + end do + close(iunit) + + ! Write analytical results + open(newunit=iunit, file='analytical_results.txt', status='replace') + write(iunit, '(A)') '# x, u (Analytical)' + do i = 1, ncells + write(iunit, '(2F12.6)') xcc(i), u_analytical(i) + end do + close(iunit) + + ! Print some statistics + print *, "==========================================" + print *, "Simulation statistics:" + print *, " ENO min/max: ", minval(u_eno), maxval(u_eno) + print *, " WENO min/max: ", minval(u_weno), maxval(u_weno) + print *, " Analytical min/max: ", minval(u_analytical), maxval(u_analytical) + print *, "==========================================" + + print *, "Simulation completed successfully!" + print *, "Results written to:" + print *, " eno_results.txt" + print *, " weno_results.txt" + print *, " analytical_results.txt" + print *, "" + print *, "To generate the comparison plot, run:" + print *, " python postprocess.py" + print *, "==========================================" + + deallocate(u_eno, u_weno, u_analytical, xcc) + end subroutine performEnoWenoAnalysis + +end module cfd_solver + +! =================================================================== +! Main Program +! =================================================================== +program main + use cfd_solver + implicit none + + print *, "==========================================" + print *, "OneFLOW-CFD Solver for 1D Convection" + print *, "ENO vs WENO Comparison" + print *, "==========================================" + + call performEnoWenoAnalysis() + + print *, "Program finished successfully!" + +end program main \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01c/postprocess.py b/example/1d-linear-convection/weno3/fortran/cfd/01c/postprocess.py new file mode 100644 index 00000000..489212dd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01c/postprocess.py @@ -0,0 +1,52 @@ +import numpy as np +import matplotlib.pyplot as plt + +def read_results(filename): + """Read results from Fortran output file""" + try: + data = np.loadtxt(filename, comments='#') + return data[:, 0], data[:, 1] + except Exception as e: + print(f"Error reading {filename}: {e}") + return np.array([]), np.array([]) + +def main(): + print("Reading Fortran output files...") + + # Read all data files + x_eno, u_eno = read_results('eno_results.txt') + x_weno, u_weno = read_results('weno_results.txt') + x_analytical, u_analytical = read_results('analytical_results.txt') + + # Check if we have data + if len(x_eno) == 0 or len(x_weno) == 0 or len(x_analytical) == 0: + print("Error: Could not read all data files.") + print("Make sure to run the Fortran program first.") + return + + # Create plot + plt.figure(figsize=(10, 6)) + + # Plot results + plt.plot(x_eno, u_eno, 'bo-', linewidth=1, markersize=3, + markerfacecolor='none', label='ENO3') + plt.plot(x_weno, u_weno, 'gs-', linewidth=1, markersize=3, + markerfacecolor='none', label='WENO3') + plt.plot(x_analytical, u_analytical, 'r-', linewidth=2, label='Analytical') + + # Customize plot + plt.title('1D Convection: ENO3 vs WENO3 (t=0.625)') + plt.xlabel('x') + plt.ylabel('u') + plt.legend() + plt.grid(True, alpha=0.3) + + # Save and show + plt.tight_layout() + plt.savefig('comparison.png', dpi=150) + plt.show() + + print("Plot saved as comparison.png") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01d/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/cfd/01d/CMakeLists.txt new file mode 100644 index 00000000..653a6751 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01d/CMakeLists.txt @@ -0,0 +1,22 @@ +# CMakeLists.txt +cmake_minimum_required(VERSION 4.2.1) # 更高版本更好支持Fortran + +project(OneFLOW_CFD LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +# 按依赖顺序显式列出源文件(解决 Intel + VS 编译顺序问题) +set(SOURCES + cfd_solver.f90 +) + +add_executable(oneflow_cfd ${SOURCES}) + +# 包含模块目录(解决 .mod 未找到) +target_include_directories(oneflow_cfd PRIVATE ${CMAKE_Fortran_MODULE_DIRECTORY}) diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01d/cfd_solver.f90 b/example/1d-linear-convection/weno3/fortran/cfd/01d/cfd_solver.f90 new file mode 100644 index 00000000..0c608e85 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01d/cfd_solver.f90 @@ -0,0 +1,941 @@ +! OneFLOW-CFD Solver for 1D Convection Equation +! ENO/WENO Reconstruction Comparison - Single File Implementation + +module cfd_solver + use, intrinsic :: iso_fortran_env, only: dp => real64 + implicit none + + private + + ! =================================================================== + ! Forward Type Declarations + ! =================================================================== + type, public :: CfdConfigType + character(len=10) :: recon_scheme = "eno" + integer :: flux_type = 0 + integer :: rk_order = 1 + integer :: spatial_order = 3 + real(dp) :: wave_speed = 1.0_dp + real(dp) :: final_time = 0.625_dp + real(dp) :: dt = 0.001_dp ! 减小时间步长 + real(dp) :: cfl = 0.5_dp ! CFL数 + end type CfdConfigType + + type, public :: MeshType + real(dp) :: xmin = 0.0_dp + real(dp) :: xmax = 2.0_dp + integer :: ncells = 40 + integer :: nnodes = 0 + integer :: nx = 0 + real(dp) :: L = 0.0_dp + real(dp) :: dx = 0.0_dp + real(dp), allocatable :: x(:), xcc(:) + contains + procedure :: init => mesh_init + end type MeshType + + type, public :: ComputationalDomainType + type(MeshType) :: mesh + type(CfdConfigType) :: config + integer :: nghosts = 0 + integer :: ist = 0 + integer :: ied = 0 + integer :: ntcells = 0 + contains + procedure :: init => domain_init + end type ComputationalDomainType + + type, public :: SolutionType + type(ComputationalDomainType) :: domain + real(dp), allocatable :: q_face_left(:), q_face_right(:) + real(dp), allocatable :: flux(:), res(:) + real(dp), allocatable :: u(:), un(:) + contains + procedure :: init => solution_init + end type SolutionType + + ! Main CFD solver class + type, public :: CfdType + type(CfdConfigType) :: config + type(ComputationalDomainType) :: domain + type(SolutionType) :: solution + class(*), allocatable :: reconstructor + contains + procedure :: init => cfd_init + end type CfdType + + ! Abstract reconstructor base class + type, abstract, public :: ReconstructorType + contains + procedure(reconstruct_interface), deferred, pass :: reconstruct + end type ReconstructorType + + ! Define abstract interface + abstract interface + subroutine reconstruct_interface(this, q, cfd) + import :: ReconstructorType, CfdType, dp + class(ReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + end subroutine reconstruct_interface + end interface + + ! ENO reconstructor + type, extends(ReconstructorType), public :: EnoReconstructorType + integer :: spatial_order + integer :: ntcells + integer, allocatable :: lmc(:) + real(dp), allocatable :: coef(:,:) + real(dp), allocatable :: dd(:,:) + contains + procedure :: reconstruct => eno_reconstruct + procedure :: init => eno_init + end type EnoReconstructorType + + ! WENO reconstructor + type, extends(ReconstructorType), public :: WenoReconstructorType + contains + procedure :: reconstruct => weno_reconstruct + end type WenoReconstructorType + + ! =================================================================== + ! Public Procedures + ! =================================================================== + public :: run_simulation, init_field, analytical_solution, performEnoWenoAnalysis + + ! =================================================================== + ! Module Variables + ! =================================================================== + real(dp), parameter :: eps_weno = 1.0e-6_dp + +contains + + ! =================================================================== + ! Initialization Methods + ! =================================================================== + + ! Mesh initialization + subroutine mesh_init(this) + class(MeshType), intent(inout) :: this + integer :: i + + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, dp) + + allocate(this%x(this%nnodes), this%xcc(this%ncells)) + + ! Node coordinates + do i = 1, this%nnodes + this%x(i) = this%xmin + (i-1) * this%dx + end do + + ! Cell center coordinates + do i = 1, this%ncells + this%xcc(i) = 0.5_dp * (this%x(i) + this%x(i+1)) + end do + end subroutine mesh_init + + ! Domain initialization + subroutine domain_init(this, mesh, config) + class(ComputationalDomainType), intent(inout) :: this + type(MeshType), intent(in) :: mesh + type(CfdConfigType), intent(in) :: config + + this%mesh = mesh + this%config = config + + ! Calculate ghost cells + if (trim(config%recon_scheme) == "eno") then + this%nghosts = config%spatial_order + else if (trim(config%recon_scheme) == "weno") then + this%nghosts = config%spatial_order / 2 + 1 + else + error stop "Unknown reconstruction scheme" + end if + + this%ist = this%nghosts + 1 + this%ied = this%ist + mesh%ncells - 1 + this%ntcells = mesh%ncells + 2 * this%nghosts + + print *, "Domain initialized:" + print *, " mesh.ncells = ", mesh%ncells + print *, " spatial_order = ", config%spatial_order + print *, " nghosts = ", this%nghosts + print *, " ist = ", this%ist, ", ied = ", this%ied + print *, " dx = ", mesh%dx + end subroutine domain_init + + ! Solution initialization + subroutine solution_init(this, domain) + class(SolutionType), intent(inout) :: this + type(ComputationalDomainType), intent(in) :: domain + + this%domain = domain + + allocate(this%q_face_left(domain%mesh%nnodes)) + allocate(this%q_face_right(domain%mesh%nnodes)) + allocate(this%flux(domain%mesh%nnodes)) + allocate(this%res(domain%mesh%ncells)) + allocate(this%u(domain%ntcells)) + allocate(this%un(domain%ntcells)) + + this%q_face_left = 0.0_dp + this%q_face_right = 0.0_dp + this%flux = 0.0_dp + this%res = 0.0_dp + this%u = 0.0_dp + this%un = 0.0_dp + end subroutine solution_init + + ! ENO reconstructor initialization + subroutine eno_init(this, spatial_order, ntcells) + class(EnoReconstructorType), intent(inout) :: this + integer, intent(in) :: spatial_order + integer, intent(in) :: ntcells + + this%spatial_order = spatial_order + this%ntcells = ntcells + + allocate(this%lmc(ntcells)) + allocate(this%coef(spatial_order+1, spatial_order)) + allocate(this%dd(spatial_order, ntcells)) + + this%lmc = 0 + this%coef = 0.0_dp + this%dd = 0.0_dp + + ! Initialize coefficients + call init_coef(spatial_order, this%coef) + end subroutine eno_init + + ! CFD solver initialization + subroutine cfd_init(this, config, domain) + class(CfdType), intent(inout) :: this + type(CfdConfigType), intent(in) :: config + type(ComputationalDomainType), intent(in) :: domain + + this%config = config + this%domain = domain + call this%solution%init(domain) + + ! Create reconstructor based on scheme + if (trim(config%recon_scheme) == "eno") then + allocate(EnoReconstructorType :: this%reconstructor) + select type(rec => this%reconstructor) + type is (EnoReconstructorType) + call rec%init(config%spatial_order, domain%ntcells) + end select + else if (trim(config%recon_scheme) == "weno") then + allocate(WenoReconstructorType :: this%reconstructor) + else + error stop "Unknown reconstruction scheme" + end if + + ! Adjust time step based on CFL condition + call calculate_dt(this) + end subroutine cfd_init + + ! Calculate time step based on CFL condition + subroutine calculate_dt(cfd) + type(CfdType), intent(inout) :: cfd + + real(dp) :: dt_cfl + + ! CFL condition: dt <= CFL * dx / |wave_speed| + dt_cfl = cfd%config%cfl * cfd%domain%mesh%dx / abs(cfd%config%wave_speed) + + if (cfd%config%dt > dt_cfl) then + print *, "Adjusting time step for stability:" + print *, " Original dt = ", cfd%config%dt + print *, " CFL dt = ", dt_cfl + cfd%config%dt = dt_cfl + print *, " Using dt = ", cfd%config%dt + end if + end subroutine calculate_dt + + ! =================================================================== + ! Initial Conditions and Analytical Solution + ! =================================================================== + + ! Initial condition: step function + function initial_condition(x) result(u0) + real(dp), intent(in) :: x + real(dp) :: u0 + + if (0.5_dp <= x .and. x <= 1.0_dp) then + u0 = 2.0_dp + else + u0 = 1.0_dp + end if + end function initial_condition + + ! Analytical solution with periodic BC + function analytical_solution(x, t, a, L) result(u) + real(dp), intent(in) :: x, t, a, L + real(dp) :: u, x_shifted + + x_shifted = mod(x - a * t + L, L) + u = initial_condition(x_shifted) + end function analytical_solution + + ! Initialize field with step function + subroutine init_field(cfd) + type(CfdType), intent(inout) :: cfd + integer :: i, j + + do i = 1, cfd%domain%mesh%ncells + if (0.5_dp <= cfd%domain%mesh%xcc(i) .and. cfd%domain%mesh%xcc(i) <= 1.0_dp) then + cfd%solution%u(cfd%domain%ist + i - 1) = 2.0_dp + else + cfd%solution%u(cfd%domain%ist + i - 1) = 1.0_dp + end if + end do + + call boundary(cfd%solution%u, cfd) + call update_oldfield(cfd%solution%un, cfd%solution%u, cfd%domain%ntcells) + end subroutine init_field + + ! =================================================================== + ! Boundary Conditions + ! =================================================================== + + ! Periodic boundary conditions - CORRECTED VERSION + subroutine periodic_boundary(u, cfd) + real(dp), intent(inout) :: u(:) + type(CfdType), intent(in) :: cfd + integer :: i + + ! Copy interior cells to ghost cells + ! Left ghost cells = right interior cells + do i = 1, cfd%domain%nghosts + u(cfd%domain%ist - i) = u(cfd%domain%ied - cfd%domain%nghosts + i - 1) + end do + + ! Right ghost cells = left interior cells + do i = 1, cfd%domain%nghosts + u(cfd%domain%ied + i) = u(cfd%domain%ist + i - 1) + end do + end subroutine periodic_boundary + + ! Boundary condition wrapper + subroutine boundary(u, cfd) + real(dp), intent(inout) :: u(:) + type(CfdType), intent(in) :: cfd + + call periodic_boundary(u, cfd) + end subroutine boundary + + ! =================================================================== + ! Reconstruction Methods + ! =================================================================== + + ! Initialize ENO/WENO coefficients + subroutine init_coef(spatial_order, coef) + integer, intent(in) :: spatial_order + real(dp), intent(out) :: coef(:,:) + + coef = 0.0_dp + + select case(spatial_order) + case(1) + coef(1,1) = 1.0_dp + coef(2,1) = 1.0_dp + + case(2) + coef(1,1:2) = [ 3.0_dp/2.0_dp, -1.0_dp/2.0_dp ] + coef(2,1:2) = [ 1.0_dp/2.0_dp, 1.0_dp/2.0_dp ] + coef(3,1:2) = [ -1.0_dp/2.0_dp, 3.0_dp/2.0_dp ] + + case(3) + coef(1,1:3) = [ 11.0_dp/6.0_dp, -7.0_dp/6.0_dp, 1.0_dp/3.0_dp ] + coef(2,1:3) = [ 1.0_dp/3.0_dp, 5.0_dp/6.0_dp, -1.0_dp/6.0_dp ] + coef(3,1:3) = [ -1.0_dp/6.0_dp, 5.0_dp/6.0_dp, 1.0_dp/3.0_dp ] + coef(4,1:3) = [ 1.0_dp/3.0_dp, -7.0_dp/6.0_dp, 11.0_dp/6.0_dp ] + + case default + error stop "Unsupported spatial order" + end select + end subroutine init_coef + + ! ENO reconstruction - SIMPLIFIED VERSION + subroutine first_order_reconstruct(this, q, cfd) + class(EnoReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + integer :: i, j + integer :: nghosts, ist, ied + + nghosts = cfd%domain%nghosts + ist = cfd%domain%ist + ied = cfd%domain%ied + + ! For now, use simple 2nd order reconstruction + do i = ist, ied + j = i - ist + 1 ! 1-based index for interfaces + + ! Simple averaging for testing + !cfd%solution%q_face_left(j) = 0.5_dp * (q(i-1) + q(i)) + !cfd%solution%q_face_right(j) = 0.5_dp * (q(i) + q(i+1)) + ! Simple averaging for testing + cfd%solution%q_face_left(j) = q(i-1) + cfd%solution%q_face_right(j) = q(i) + end do + end subroutine first_order_reconstruct + + subroutine eno_reconstruct(this, q, cfd) + class(EnoReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + type(ComputationalDomainType), pointer :: domain + + integer :: i, j, m, k1, k2, r1, r2 + integer :: nghosts, ist, ied + + nghosts = cfd%domain%nghosts + ist = cfd%domain%ist + ied = cfd%domain%ied + + ! 1. 差商计算 (dd[1,:] = q) + this%dd(1, :) = q(:) + do m = 2, this%spatial_order + do j = 1, cfd%domain%ntcells - m + 1 + this%dd(m, j) = this%dd(m-1, j+1) - this%dd(m-1, j) + end do + end do + + ! 2. 选择 smoothest stencil + do i = ist - 1, ied ! Python: range(ist-1, ied+1) → ied+1-1 = ied + this%lmc(i) = i + do m = 2, this%spatial_order + if ( abs(this%dd(m, this%lmc(i) - 1) ) < abs(this%dd(m, this%lmc(i)))) then + this%lmc(i) = this%lmc(i) - 1 + end if + end do + end do + + associate ( & + q_face_left => cfd%solution%q_face_left, & + q_face_right => cfd%solution%q_face_right & + ) + ! 这里可以直接使用 q_face_left 和 q_face_right + ! 3. 重构界面值 + do i = ist, ied + j = i - ist + 1 ! 1-based index for interfaces + k1 = this%lmc(i - 1) + k2 = this%lmc(i) + r1 = (i - 1) - k1 + 1 + r2 = i - k2 + 1 + q_face_left(j) = 0.0 + q_face_right(j) = 0.0 + do m = 1, this%spatial_order + q_face_left(j) = q_face_left(j) + q(k1 + m - 1) * this%coef(r1 + 1, m) + q_face_right(j) = q_face_right(j) + q(k2 + m - 1) * this%coef(r2, m) + end do + end do + end associate + + end subroutine eno_reconstruct + + ! WENO-3 nonlinear weights for left-biased stencil + function wc3L(v1, v2, v3) result(f) + real(dp), intent(in) :: v1, v2, v3 + real(dp) :: f, s0, s1, d0, d1, c0, c1, w0, w1, q0, q1 + + ! Smoothness indicators + s0 = (v3 - v2)**2 + s1 = (v2 - v1)**2 + + ! Nonlinear weights + d0 = 2.0_dp/3.0_dp + d1 = 1.0_dp/3.0_dp + + c0 = d0 / ((eps_weno + s0)**2) + c1 = d1 / ((eps_weno + s1)**2) + + w0 = c0 / (c0 + c1) + w1 = c1 / (c0 + c1) + + ! Candidate stencils + q0 = 0.5_dp * v2 + 0.5_dp * v3 + q1 = -0.5_dp * v1 + 1.5_dp * v2 + + ! Reconstructed value + f = w0 * q0 + w1 * q1 + end function wc3L + + ! WENO-3 nonlinear weights for right-biased stencil + function wc3R(v1, v2, v3) result(f) + real(dp), intent(in) :: v1, v2, v3 + real(dp) :: f, s0, s1, d0, d1, c0, c1, w0, w1, q0, q1 + + ! Smoothness indicators + s0 = (v2 - v1)**2 + s1 = (v3 - v2)**2 + + ! Nonlinear weights + d0 = 2.0_dp/3.0_dp + d1 = 1.0_dp/3.0_dp + + c0 = d0 / ((eps_weno + s0)**2) + c1 = d1 / ((eps_weno + s1)**2) + + w0 = c0 / (c0 + c1) + w1 = c1 / (c0 + c1) + + ! Candidate stencils + q0 = 0.5_dp * v1 + 0.5_dp * v2 + q1 = 1.5_dp * v2 - 0.5_dp * v3 + + ! Reconstructed value + f = w0 * q0 + w1 * q1 + end function wc3R + + ! WENO-3 reconstruction for left interface + subroutine weno3L_periodic(cfd, u, qL) + type(CfdType), intent(in) :: cfd + real(dp), intent(in) :: u(:) + real(dp), intent(out) :: qL(:) + + integer :: i, j, nghosts, ist, ied + + nghosts = cfd%domain%nghosts + ist = cfd%domain%ist + ied = cfd%domain%ied + + do i = ist-1, ied-1 + j = i - (ist - 1) + 1 + qL(j) = wc3L(u(i-1), u(i), u(i+1)) + end do + + end subroutine weno3L_periodic + + ! WENO-3 reconstruction for right interface + subroutine weno3R_periodic(cfd, u, qR) + type(CfdType), intent(in) :: cfd + real(dp), intent(in) :: u(:) + real(dp), intent(out) :: qR(:) + + integer :: i, j, nghosts, ist, ied + + nghosts = cfd%domain%nghosts + ist = cfd%domain%ist + ied = cfd%domain%ied + + do i = ist, ied + j = i - ist + 1 + qR(j) = wc3R(u(i-1), u(i), u(i+1)) + end do + + end subroutine weno3R_periodic + + ! WENO reconstruction + subroutine weno_reconstruct(this, q, cfd) + class(WenoReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + call weno3L_periodic(cfd, q, cfd%solution%q_face_left) + call weno3R_periodic(cfd, q, cfd%solution%q_face_right) + end subroutine weno_reconstruct + + ! General reconstruction wrapper + subroutine reconstruction(q, cfd) + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + select type(rec => cfd%reconstructor) + type is (EnoReconstructorType) + call rec%reconstruct(q, cfd) + type is (WenoReconstructorType) + call rec%reconstruct(q, cfd) + class default + error stop "Unknown reconstructor type" + end select + end subroutine reconstruction + + ! =================================================================== + ! Flux Functions + ! =================================================================== + + ! Rusanov flux + subroutine rusanov_flux(q_face_left, q_face_right, flux, cfd) + real(dp), intent(in) :: q_face_left(:), q_face_right(:) + real(dp), intent(out) :: flux(:) + type(CfdType), intent(in) :: cfd + + integer :: i + real(dp) :: u_L, u_R, F_L, F_R, c_L, c_R, Smax + + c_L = cfd%config%wave_speed + c_R = cfd%config%wave_speed + + do i = 1, cfd%domain%mesh%nnodes + u_L = q_face_left(i) + u_R = q_face_right(i) + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux(i) = 0.5_dp * (F_L + F_R) - 0.5_dp * Smax * (u_R - u_L) + end do + end subroutine rusanov_flux + + ! Engquist-Osher flux + subroutine engquist_osher_flux(q_face_left, q_face_right, flux, cfd) + real(dp), intent(in) :: q_face_left(:), q_face_right(:) + real(dp), intent(out) :: flux(:) + type(CfdType), intent(in) :: cfd + + integer :: i + real(dp) :: c, cp, cm, u_L, u_R + + c = cfd%config%wave_speed + + do i = 1, cfd%domain%mesh%nnodes + cp = 0.5_dp * (c + abs(c)) + cm = 0.5_dp * (c - abs(c)) + u_L = q_face_left(i) + u_R = q_face_right(i) + flux(i) = cp * u_L + cm * u_R + end do + end subroutine engquist_osher_flux + + ! Inviscid flux selection + subroutine inviscid_flux(q_face_left, q_face_right, flux, cfd) + real(dp), intent(in) :: q_face_left(:), q_face_right(:) + real(dp), intent(out) :: flux(:) + type(CfdType), intent(in) :: cfd + + if (cfd%config%flux_type == 0) then + call rusanov_flux(q_face_left, q_face_right, flux, cfd) + else + call engquist_osher_flux(q_face_left, q_face_right, flux, cfd) + end if + end subroutine inviscid_flux + + ! =================================================================== + ! Residual Computation + ! =================================================================== + + ! Compute residual (flux divergence) - CORRECTED VERSION + subroutine residual(q, cfd) + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + integer :: i + + ! Apply boundary conditions first + call boundary(cfd%solution%u, cfd) + + ! Reconstruction + call reconstruction(q, cfd) + + ! Compute fluxes + call inviscid_flux(cfd%solution%q_face_left, cfd%solution%q_face_right, & + cfd%solution%flux, cfd) + + ! Compute residual - corrected indexing + do i = 1, cfd%domain%mesh%ncells + cfd%solution%res(i) = -(cfd%solution%flux(i+1) - cfd%solution%flux(i)) / & + cfd%domain%mesh%dx + end do + end subroutine residual + + ! =================================================================== + ! Time Integration + ! =================================================================== + + ! Update old field + subroutine update_oldfield(qn, q, n) + real(dp), intent(out) :: qn(:) + real(dp), intent(in) :: q(:) + integer, intent(in) :: n + + qn(1:n) = q(1:n) + end subroutine update_oldfield + + ! 1st-order Runge-Kutta (Euler) - SIMPLIFIED + subroutine runge_kutta_1(cfd) + type(CfdType), intent(inout) :: cfd + + integer :: i, j + real(dp) :: dt + + dt = cfd%config%dt + + ! Apply boundary conditions + call boundary(cfd%solution%u, cfd) + + ! Compute residual + call residual(cfd%solution%u, cfd) + + ! Update solution + do i = cfd%domain%ist, cfd%domain%ied - 1 + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = cfd%solution%u(i) + dt * cfd%solution%res(j) + end do + + ! Apply boundary conditions again + call boundary(cfd%solution%u, cfd) + + ! Save old solution + call update_oldfield(cfd%solution%un, cfd%solution%u, cfd%domain%ntcells) + end subroutine runge_kutta_1 + + ! 2nd-order Runge-Kutta (Heun) + subroutine runge_kutta_2(cfd) + type(CfdType), intent(inout) :: cfd + + integer :: i, j + real(dp) :: dt + + dt = cfd%config%dt + + ! Stage 1 + call boundary(cfd%solution%u, cfd) + call residual(cfd%solution%u, cfd) + + do i = cfd%domain%ist, cfd%domain%ied - 1 + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = cfd%solution%u(i) + dt * cfd%solution%res(j) + end do + call boundary(cfd%solution%u, cfd) + + ! Stage 2 + call residual(cfd%solution%u, cfd) + do i = cfd%domain%ist, cfd%domain%ied - 1 + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = 0.5_dp * cfd%solution%un(i) + & + 0.5_dp * cfd%solution%u(i) + & + 0.5_dp * dt * cfd%solution%res(j) + end do + call boundary(cfd%solution%u, cfd) + + call update_oldfield(cfd%solution%un, cfd%solution%u, cfd%domain%ntcells) + end subroutine runge_kutta_2 + + ! Runge-Kutta selection + subroutine runge_kutta(cfd) + type(CfdType), intent(inout) :: cfd + + select case(cfd%config%rk_order) + case(1) + call runge_kutta_1(cfd) + case(2) + call runge_kutta_2(cfd) + case default + call runge_kutta_1(cfd) + end select + end subroutine runge_kutta + + ! =================================================================== + ! Simulation Driver + ! =================================================================== + + ! Run simulation to final time + function run_simulation(cfd, final_time) result(u_result) + type(CfdType), intent(inout) :: cfd + real(dp), intent(in) :: final_time + real(dp), allocatable :: u_result(:) + + real(dp) :: t, dt, dt_old + integer :: step, max_steps + + allocate(u_result(cfd%domain%mesh%ncells)) + + t = 0.0_dp + dt_old = cfd%config%dt + dt = dt_old + max_steps = 10000 ! Safety limit + + print *, "Starting time integration..." + print *, " Final time: ", final_time + print *, " Time step: ", dt + print *, " CFL number: ", cfd%config%cfl + + step = 0 + do while (t < final_time - 1.0e-12_dp .and. step < max_steps) + step = step + 1 + + if (t + dt > final_time) then + dt = final_time - t + end if + + cfd%config%dt = dt + call runge_kutta(cfd) + t = t + dt + + ! Progress report + if (mod(step, 100) == 0) then + print *, " Step ", step, ", Time = ", t + end if + end do + + if (step >= max_steps) then + print *, "Warning: Reached maximum number of steps (", max_steps, ")" + end if + + cfd%config%dt = dt_old + + print *, "Time integration completed:" + print *, " Total steps: ", step + print *, " Final time: ", t + + ! Extract physical solution (without ghost cells) + u_result = cfd%solution%u(cfd%domain%ist:cfd%domain%ied) + end function run_simulation + + ! =================================================================== + ! Main Analysis Function + ! =================================================================== + + ! Perform ENO-WENO comparative analysis + subroutine performEnoWenoAnalysis() + type(CfdConfigType) :: config_eno3, config_weno3 + type(MeshType) :: mesh + type(ComputationalDomainType) :: domain_eno3, domain_weno3 + type(CfdType) :: cfd_eno3, cfd_weno3 + real(dp), allocatable :: u_eno(:), u_weno(:), u_analytical(:) + real(dp), allocatable :: xcc(:) + integer :: i, ncells, iunit + + ! Initialize mesh + call mesh%init() + ncells = mesh%ncells + allocate(xcc(ncells)) + xcc = mesh%xcc + + print *, "==========================================" + print *, "Mesh parameters:" + print *, " ncells = ", ncells + print *, " dx = ", mesh%dx + print *, " L = ", mesh%L + print *, "==========================================" + + ! Configure ENO3 - using simple 2nd order for stability + config_eno3%recon_scheme = "eno" + config_eno3%spatial_order = 2 ! Use 2nd order for stability + config_eno3%flux_type = 0 + config_eno3%rk_order = 1 + config_eno3%wave_speed = 1.0_dp + config_eno3%final_time = 0.625_dp + config_eno3%cfl = 0.5_dp + config_eno3%dt = 0.001_dp ! Small time step + + ! Configure WENO3 + config_weno3%recon_scheme = "weno" + config_weno3%spatial_order = 3 + config_weno3%flux_type = 0 + config_weno3%rk_order = 1 + config_weno3%wave_speed = 1.0_dp + config_weno3%final_time = 0.625_dp + config_weno3%cfl = 0.3_dp ! More strict CFL for WENO + config_weno3%dt = 0.0005_dp ! Even smaller time step + + ! Create domains + call domain_eno3%init(mesh, config_eno3) + call domain_weno3%init(mesh, config_weno3) + + ! Create CFD solvers + call cfd_eno3%init(config_eno3, domain_eno3) + call cfd_weno3%init(config_weno3, domain_weno3) + + ! Allocate arrays + allocate(u_eno(ncells), u_weno(ncells), u_analytical(ncells)) + + ! Run ENO simulation + print *, "==========================================" + print *, "Running ENO simulation..." + print *, " Scheme: ENO", config_eno3%spatial_order + print *, " Time step: ", config_eno3%dt + print *, "==========================================" + + call init_field(cfd_eno3) + u_eno = run_simulation(cfd_eno3, config_eno3%final_time) + + ! Run WENO simulation + print *, "==========================================" + print *, "Running WENO simulation..." + print *, " Scheme: WENO", config_weno3%spatial_order + print *, " Time step: ", config_weno3%dt + print *, "==========================================" + + call init_field(cfd_weno3) + u_weno = run_simulation(cfd_weno3, config_weno3%final_time) + + ! Compute analytical solution + print *, "Computing analytical solution..." + do i = 1, ncells + u_analytical(i) = analytical_solution(xcc(i), config_weno3%final_time, & + config_weno3%wave_speed, mesh%L) + end do + + ! Write results to files + print *, "Writing results to files..." + + ! Write ENO results + open(newunit=iunit, file='eno_results.txt', status='replace') + write(iunit, '(A)') '# x, u (ENO)' + do i = 1, ncells + write(iunit, '(2F12.6)') xcc(i), u_eno(i) + end do + close(iunit) + + ! Write WENO results + open(newunit=iunit, file='weno_results.txt', status='replace') + write(iunit, '(A)') '# x, u (WENO)' + do i = 1, ncells + write(iunit, '(2F12.6)') xcc(i), u_weno(i) + end do + close(iunit) + + ! Write analytical results + open(newunit=iunit, file='analytical_results.txt', status='replace') + write(iunit, '(A)') '# x, u (Analytical)' + do i = 1, ncells + write(iunit, '(2F12.6)') xcc(i), u_analytical(i) + end do + close(iunit) + + ! Print some statistics + print *, "==========================================" + print *, "Simulation statistics:" + print *, " ENO min/max: ", minval(u_eno), maxval(u_eno) + print *, " WENO min/max: ", minval(u_weno), maxval(u_weno) + print *, " Analytical min/max: ", minval(u_analytical), maxval(u_analytical) + print *, "==========================================" + + print *, "Simulation completed successfully!" + print *, "Results written to:" + print *, " eno_results.txt" + print *, " weno_results.txt" + print *, " analytical_results.txt" + print *, "" + print *, "To generate the comparison plot, run:" + print *, " python postprocess.py" + print *, "==========================================" + + deallocate(u_eno, u_weno, u_analytical, xcc) + end subroutine performEnoWenoAnalysis + +end module cfd_solver + +! =================================================================== +! Main Program +! =================================================================== +program main + use cfd_solver + implicit none + + print *, "==========================================" + print *, "OneFLOW-CFD Solver for 1D Convection" + print *, "ENO vs WENO Comparison" + print *, "==========================================" + + call performEnoWenoAnalysis() + + print *, "Program finished successfully!" + +end program main \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01d/postprocess.py b/example/1d-linear-convection/weno3/fortran/cfd/01d/postprocess.py new file mode 100644 index 00000000..489212dd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01d/postprocess.py @@ -0,0 +1,52 @@ +import numpy as np +import matplotlib.pyplot as plt + +def read_results(filename): + """Read results from Fortran output file""" + try: + data = np.loadtxt(filename, comments='#') + return data[:, 0], data[:, 1] + except Exception as e: + print(f"Error reading {filename}: {e}") + return np.array([]), np.array([]) + +def main(): + print("Reading Fortran output files...") + + # Read all data files + x_eno, u_eno = read_results('eno_results.txt') + x_weno, u_weno = read_results('weno_results.txt') + x_analytical, u_analytical = read_results('analytical_results.txt') + + # Check if we have data + if len(x_eno) == 0 or len(x_weno) == 0 or len(x_analytical) == 0: + print("Error: Could not read all data files.") + print("Make sure to run the Fortran program first.") + return + + # Create plot + plt.figure(figsize=(10, 6)) + + # Plot results + plt.plot(x_eno, u_eno, 'bo-', linewidth=1, markersize=3, + markerfacecolor='none', label='ENO3') + plt.plot(x_weno, u_weno, 'gs-', linewidth=1, markersize=3, + markerfacecolor='none', label='WENO3') + plt.plot(x_analytical, u_analytical, 'r-', linewidth=2, label='Analytical') + + # Customize plot + plt.title('1D Convection: ENO3 vs WENO3 (t=0.625)') + plt.xlabel('x') + plt.ylabel('u') + plt.legend() + plt.grid(True, alpha=0.3) + + # Save and show + plt.tight_layout() + plt.savefig('comparison.png', dpi=150) + plt.show() + + print("Plot saved as comparison.png") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01e/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/cfd/01e/CMakeLists.txt new file mode 100644 index 00000000..653a6751 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01e/CMakeLists.txt @@ -0,0 +1,22 @@ +# CMakeLists.txt +cmake_minimum_required(VERSION 4.2.1) # 更高版本更好支持Fortran + +project(OneFLOW_CFD LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +# 按依赖顺序显式列出源文件(解决 Intel + VS 编译顺序问题) +set(SOURCES + cfd_solver.f90 +) + +add_executable(oneflow_cfd ${SOURCES}) + +# 包含模块目录(解决 .mod 未找到) +target_include_directories(oneflow_cfd PRIVATE ${CMAKE_Fortran_MODULE_DIRECTORY}) diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01e/cfd_solver.f90 b/example/1d-linear-convection/weno3/fortran/cfd/01e/cfd_solver.f90 new file mode 100644 index 00000000..0c608e85 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01e/cfd_solver.f90 @@ -0,0 +1,941 @@ +! OneFLOW-CFD Solver for 1D Convection Equation +! ENO/WENO Reconstruction Comparison - Single File Implementation + +module cfd_solver + use, intrinsic :: iso_fortran_env, only: dp => real64 + implicit none + + private + + ! =================================================================== + ! Forward Type Declarations + ! =================================================================== + type, public :: CfdConfigType + character(len=10) :: recon_scheme = "eno" + integer :: flux_type = 0 + integer :: rk_order = 1 + integer :: spatial_order = 3 + real(dp) :: wave_speed = 1.0_dp + real(dp) :: final_time = 0.625_dp + real(dp) :: dt = 0.001_dp ! 减小时间步长 + real(dp) :: cfl = 0.5_dp ! CFL数 + end type CfdConfigType + + type, public :: MeshType + real(dp) :: xmin = 0.0_dp + real(dp) :: xmax = 2.0_dp + integer :: ncells = 40 + integer :: nnodes = 0 + integer :: nx = 0 + real(dp) :: L = 0.0_dp + real(dp) :: dx = 0.0_dp + real(dp), allocatable :: x(:), xcc(:) + contains + procedure :: init => mesh_init + end type MeshType + + type, public :: ComputationalDomainType + type(MeshType) :: mesh + type(CfdConfigType) :: config + integer :: nghosts = 0 + integer :: ist = 0 + integer :: ied = 0 + integer :: ntcells = 0 + contains + procedure :: init => domain_init + end type ComputationalDomainType + + type, public :: SolutionType + type(ComputationalDomainType) :: domain + real(dp), allocatable :: q_face_left(:), q_face_right(:) + real(dp), allocatable :: flux(:), res(:) + real(dp), allocatable :: u(:), un(:) + contains + procedure :: init => solution_init + end type SolutionType + + ! Main CFD solver class + type, public :: CfdType + type(CfdConfigType) :: config + type(ComputationalDomainType) :: domain + type(SolutionType) :: solution + class(*), allocatable :: reconstructor + contains + procedure :: init => cfd_init + end type CfdType + + ! Abstract reconstructor base class + type, abstract, public :: ReconstructorType + contains + procedure(reconstruct_interface), deferred, pass :: reconstruct + end type ReconstructorType + + ! Define abstract interface + abstract interface + subroutine reconstruct_interface(this, q, cfd) + import :: ReconstructorType, CfdType, dp + class(ReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + end subroutine reconstruct_interface + end interface + + ! ENO reconstructor + type, extends(ReconstructorType), public :: EnoReconstructorType + integer :: spatial_order + integer :: ntcells + integer, allocatable :: lmc(:) + real(dp), allocatable :: coef(:,:) + real(dp), allocatable :: dd(:,:) + contains + procedure :: reconstruct => eno_reconstruct + procedure :: init => eno_init + end type EnoReconstructorType + + ! WENO reconstructor + type, extends(ReconstructorType), public :: WenoReconstructorType + contains + procedure :: reconstruct => weno_reconstruct + end type WenoReconstructorType + + ! =================================================================== + ! Public Procedures + ! =================================================================== + public :: run_simulation, init_field, analytical_solution, performEnoWenoAnalysis + + ! =================================================================== + ! Module Variables + ! =================================================================== + real(dp), parameter :: eps_weno = 1.0e-6_dp + +contains + + ! =================================================================== + ! Initialization Methods + ! =================================================================== + + ! Mesh initialization + subroutine mesh_init(this) + class(MeshType), intent(inout) :: this + integer :: i + + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, dp) + + allocate(this%x(this%nnodes), this%xcc(this%ncells)) + + ! Node coordinates + do i = 1, this%nnodes + this%x(i) = this%xmin + (i-1) * this%dx + end do + + ! Cell center coordinates + do i = 1, this%ncells + this%xcc(i) = 0.5_dp * (this%x(i) + this%x(i+1)) + end do + end subroutine mesh_init + + ! Domain initialization + subroutine domain_init(this, mesh, config) + class(ComputationalDomainType), intent(inout) :: this + type(MeshType), intent(in) :: mesh + type(CfdConfigType), intent(in) :: config + + this%mesh = mesh + this%config = config + + ! Calculate ghost cells + if (trim(config%recon_scheme) == "eno") then + this%nghosts = config%spatial_order + else if (trim(config%recon_scheme) == "weno") then + this%nghosts = config%spatial_order / 2 + 1 + else + error stop "Unknown reconstruction scheme" + end if + + this%ist = this%nghosts + 1 + this%ied = this%ist + mesh%ncells - 1 + this%ntcells = mesh%ncells + 2 * this%nghosts + + print *, "Domain initialized:" + print *, " mesh.ncells = ", mesh%ncells + print *, " spatial_order = ", config%spatial_order + print *, " nghosts = ", this%nghosts + print *, " ist = ", this%ist, ", ied = ", this%ied + print *, " dx = ", mesh%dx + end subroutine domain_init + + ! Solution initialization + subroutine solution_init(this, domain) + class(SolutionType), intent(inout) :: this + type(ComputationalDomainType), intent(in) :: domain + + this%domain = domain + + allocate(this%q_face_left(domain%mesh%nnodes)) + allocate(this%q_face_right(domain%mesh%nnodes)) + allocate(this%flux(domain%mesh%nnodes)) + allocate(this%res(domain%mesh%ncells)) + allocate(this%u(domain%ntcells)) + allocate(this%un(domain%ntcells)) + + this%q_face_left = 0.0_dp + this%q_face_right = 0.0_dp + this%flux = 0.0_dp + this%res = 0.0_dp + this%u = 0.0_dp + this%un = 0.0_dp + end subroutine solution_init + + ! ENO reconstructor initialization + subroutine eno_init(this, spatial_order, ntcells) + class(EnoReconstructorType), intent(inout) :: this + integer, intent(in) :: spatial_order + integer, intent(in) :: ntcells + + this%spatial_order = spatial_order + this%ntcells = ntcells + + allocate(this%lmc(ntcells)) + allocate(this%coef(spatial_order+1, spatial_order)) + allocate(this%dd(spatial_order, ntcells)) + + this%lmc = 0 + this%coef = 0.0_dp + this%dd = 0.0_dp + + ! Initialize coefficients + call init_coef(spatial_order, this%coef) + end subroutine eno_init + + ! CFD solver initialization + subroutine cfd_init(this, config, domain) + class(CfdType), intent(inout) :: this + type(CfdConfigType), intent(in) :: config + type(ComputationalDomainType), intent(in) :: domain + + this%config = config + this%domain = domain + call this%solution%init(domain) + + ! Create reconstructor based on scheme + if (trim(config%recon_scheme) == "eno") then + allocate(EnoReconstructorType :: this%reconstructor) + select type(rec => this%reconstructor) + type is (EnoReconstructorType) + call rec%init(config%spatial_order, domain%ntcells) + end select + else if (trim(config%recon_scheme) == "weno") then + allocate(WenoReconstructorType :: this%reconstructor) + else + error stop "Unknown reconstruction scheme" + end if + + ! Adjust time step based on CFL condition + call calculate_dt(this) + end subroutine cfd_init + + ! Calculate time step based on CFL condition + subroutine calculate_dt(cfd) + type(CfdType), intent(inout) :: cfd + + real(dp) :: dt_cfl + + ! CFL condition: dt <= CFL * dx / |wave_speed| + dt_cfl = cfd%config%cfl * cfd%domain%mesh%dx / abs(cfd%config%wave_speed) + + if (cfd%config%dt > dt_cfl) then + print *, "Adjusting time step for stability:" + print *, " Original dt = ", cfd%config%dt + print *, " CFL dt = ", dt_cfl + cfd%config%dt = dt_cfl + print *, " Using dt = ", cfd%config%dt + end if + end subroutine calculate_dt + + ! =================================================================== + ! Initial Conditions and Analytical Solution + ! =================================================================== + + ! Initial condition: step function + function initial_condition(x) result(u0) + real(dp), intent(in) :: x + real(dp) :: u0 + + if (0.5_dp <= x .and. x <= 1.0_dp) then + u0 = 2.0_dp + else + u0 = 1.0_dp + end if + end function initial_condition + + ! Analytical solution with periodic BC + function analytical_solution(x, t, a, L) result(u) + real(dp), intent(in) :: x, t, a, L + real(dp) :: u, x_shifted + + x_shifted = mod(x - a * t + L, L) + u = initial_condition(x_shifted) + end function analytical_solution + + ! Initialize field with step function + subroutine init_field(cfd) + type(CfdType), intent(inout) :: cfd + integer :: i, j + + do i = 1, cfd%domain%mesh%ncells + if (0.5_dp <= cfd%domain%mesh%xcc(i) .and. cfd%domain%mesh%xcc(i) <= 1.0_dp) then + cfd%solution%u(cfd%domain%ist + i - 1) = 2.0_dp + else + cfd%solution%u(cfd%domain%ist + i - 1) = 1.0_dp + end if + end do + + call boundary(cfd%solution%u, cfd) + call update_oldfield(cfd%solution%un, cfd%solution%u, cfd%domain%ntcells) + end subroutine init_field + + ! =================================================================== + ! Boundary Conditions + ! =================================================================== + + ! Periodic boundary conditions - CORRECTED VERSION + subroutine periodic_boundary(u, cfd) + real(dp), intent(inout) :: u(:) + type(CfdType), intent(in) :: cfd + integer :: i + + ! Copy interior cells to ghost cells + ! Left ghost cells = right interior cells + do i = 1, cfd%domain%nghosts + u(cfd%domain%ist - i) = u(cfd%domain%ied - cfd%domain%nghosts + i - 1) + end do + + ! Right ghost cells = left interior cells + do i = 1, cfd%domain%nghosts + u(cfd%domain%ied + i) = u(cfd%domain%ist + i - 1) + end do + end subroutine periodic_boundary + + ! Boundary condition wrapper + subroutine boundary(u, cfd) + real(dp), intent(inout) :: u(:) + type(CfdType), intent(in) :: cfd + + call periodic_boundary(u, cfd) + end subroutine boundary + + ! =================================================================== + ! Reconstruction Methods + ! =================================================================== + + ! Initialize ENO/WENO coefficients + subroutine init_coef(spatial_order, coef) + integer, intent(in) :: spatial_order + real(dp), intent(out) :: coef(:,:) + + coef = 0.0_dp + + select case(spatial_order) + case(1) + coef(1,1) = 1.0_dp + coef(2,1) = 1.0_dp + + case(2) + coef(1,1:2) = [ 3.0_dp/2.0_dp, -1.0_dp/2.0_dp ] + coef(2,1:2) = [ 1.0_dp/2.0_dp, 1.0_dp/2.0_dp ] + coef(3,1:2) = [ -1.0_dp/2.0_dp, 3.0_dp/2.0_dp ] + + case(3) + coef(1,1:3) = [ 11.0_dp/6.0_dp, -7.0_dp/6.0_dp, 1.0_dp/3.0_dp ] + coef(2,1:3) = [ 1.0_dp/3.0_dp, 5.0_dp/6.0_dp, -1.0_dp/6.0_dp ] + coef(3,1:3) = [ -1.0_dp/6.0_dp, 5.0_dp/6.0_dp, 1.0_dp/3.0_dp ] + coef(4,1:3) = [ 1.0_dp/3.0_dp, -7.0_dp/6.0_dp, 11.0_dp/6.0_dp ] + + case default + error stop "Unsupported spatial order" + end select + end subroutine init_coef + + ! ENO reconstruction - SIMPLIFIED VERSION + subroutine first_order_reconstruct(this, q, cfd) + class(EnoReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + integer :: i, j + integer :: nghosts, ist, ied + + nghosts = cfd%domain%nghosts + ist = cfd%domain%ist + ied = cfd%domain%ied + + ! For now, use simple 2nd order reconstruction + do i = ist, ied + j = i - ist + 1 ! 1-based index for interfaces + + ! Simple averaging for testing + !cfd%solution%q_face_left(j) = 0.5_dp * (q(i-1) + q(i)) + !cfd%solution%q_face_right(j) = 0.5_dp * (q(i) + q(i+1)) + ! Simple averaging for testing + cfd%solution%q_face_left(j) = q(i-1) + cfd%solution%q_face_right(j) = q(i) + end do + end subroutine first_order_reconstruct + + subroutine eno_reconstruct(this, q, cfd) + class(EnoReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + type(ComputationalDomainType), pointer :: domain + + integer :: i, j, m, k1, k2, r1, r2 + integer :: nghosts, ist, ied + + nghosts = cfd%domain%nghosts + ist = cfd%domain%ist + ied = cfd%domain%ied + + ! 1. 差商计算 (dd[1,:] = q) + this%dd(1, :) = q(:) + do m = 2, this%spatial_order + do j = 1, cfd%domain%ntcells - m + 1 + this%dd(m, j) = this%dd(m-1, j+1) - this%dd(m-1, j) + end do + end do + + ! 2. 选择 smoothest stencil + do i = ist - 1, ied ! Python: range(ist-1, ied+1) → ied+1-1 = ied + this%lmc(i) = i + do m = 2, this%spatial_order + if ( abs(this%dd(m, this%lmc(i) - 1) ) < abs(this%dd(m, this%lmc(i)))) then + this%lmc(i) = this%lmc(i) - 1 + end if + end do + end do + + associate ( & + q_face_left => cfd%solution%q_face_left, & + q_face_right => cfd%solution%q_face_right & + ) + ! 这里可以直接使用 q_face_left 和 q_face_right + ! 3. 重构界面值 + do i = ist, ied + j = i - ist + 1 ! 1-based index for interfaces + k1 = this%lmc(i - 1) + k2 = this%lmc(i) + r1 = (i - 1) - k1 + 1 + r2 = i - k2 + 1 + q_face_left(j) = 0.0 + q_face_right(j) = 0.0 + do m = 1, this%spatial_order + q_face_left(j) = q_face_left(j) + q(k1 + m - 1) * this%coef(r1 + 1, m) + q_face_right(j) = q_face_right(j) + q(k2 + m - 1) * this%coef(r2, m) + end do + end do + end associate + + end subroutine eno_reconstruct + + ! WENO-3 nonlinear weights for left-biased stencil + function wc3L(v1, v2, v3) result(f) + real(dp), intent(in) :: v1, v2, v3 + real(dp) :: f, s0, s1, d0, d1, c0, c1, w0, w1, q0, q1 + + ! Smoothness indicators + s0 = (v3 - v2)**2 + s1 = (v2 - v1)**2 + + ! Nonlinear weights + d0 = 2.0_dp/3.0_dp + d1 = 1.0_dp/3.0_dp + + c0 = d0 / ((eps_weno + s0)**2) + c1 = d1 / ((eps_weno + s1)**2) + + w0 = c0 / (c0 + c1) + w1 = c1 / (c0 + c1) + + ! Candidate stencils + q0 = 0.5_dp * v2 + 0.5_dp * v3 + q1 = -0.5_dp * v1 + 1.5_dp * v2 + + ! Reconstructed value + f = w0 * q0 + w1 * q1 + end function wc3L + + ! WENO-3 nonlinear weights for right-biased stencil + function wc3R(v1, v2, v3) result(f) + real(dp), intent(in) :: v1, v2, v3 + real(dp) :: f, s0, s1, d0, d1, c0, c1, w0, w1, q0, q1 + + ! Smoothness indicators + s0 = (v2 - v1)**2 + s1 = (v3 - v2)**2 + + ! Nonlinear weights + d0 = 2.0_dp/3.0_dp + d1 = 1.0_dp/3.0_dp + + c0 = d0 / ((eps_weno + s0)**2) + c1 = d1 / ((eps_weno + s1)**2) + + w0 = c0 / (c0 + c1) + w1 = c1 / (c0 + c1) + + ! Candidate stencils + q0 = 0.5_dp * v1 + 0.5_dp * v2 + q1 = 1.5_dp * v2 - 0.5_dp * v3 + + ! Reconstructed value + f = w0 * q0 + w1 * q1 + end function wc3R + + ! WENO-3 reconstruction for left interface + subroutine weno3L_periodic(cfd, u, qL) + type(CfdType), intent(in) :: cfd + real(dp), intent(in) :: u(:) + real(dp), intent(out) :: qL(:) + + integer :: i, j, nghosts, ist, ied + + nghosts = cfd%domain%nghosts + ist = cfd%domain%ist + ied = cfd%domain%ied + + do i = ist-1, ied-1 + j = i - (ist - 1) + 1 + qL(j) = wc3L(u(i-1), u(i), u(i+1)) + end do + + end subroutine weno3L_periodic + + ! WENO-3 reconstruction for right interface + subroutine weno3R_periodic(cfd, u, qR) + type(CfdType), intent(in) :: cfd + real(dp), intent(in) :: u(:) + real(dp), intent(out) :: qR(:) + + integer :: i, j, nghosts, ist, ied + + nghosts = cfd%domain%nghosts + ist = cfd%domain%ist + ied = cfd%domain%ied + + do i = ist, ied + j = i - ist + 1 + qR(j) = wc3R(u(i-1), u(i), u(i+1)) + end do + + end subroutine weno3R_periodic + + ! WENO reconstruction + subroutine weno_reconstruct(this, q, cfd) + class(WenoReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + call weno3L_periodic(cfd, q, cfd%solution%q_face_left) + call weno3R_periodic(cfd, q, cfd%solution%q_face_right) + end subroutine weno_reconstruct + + ! General reconstruction wrapper + subroutine reconstruction(q, cfd) + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + select type(rec => cfd%reconstructor) + type is (EnoReconstructorType) + call rec%reconstruct(q, cfd) + type is (WenoReconstructorType) + call rec%reconstruct(q, cfd) + class default + error stop "Unknown reconstructor type" + end select + end subroutine reconstruction + + ! =================================================================== + ! Flux Functions + ! =================================================================== + + ! Rusanov flux + subroutine rusanov_flux(q_face_left, q_face_right, flux, cfd) + real(dp), intent(in) :: q_face_left(:), q_face_right(:) + real(dp), intent(out) :: flux(:) + type(CfdType), intent(in) :: cfd + + integer :: i + real(dp) :: u_L, u_R, F_L, F_R, c_L, c_R, Smax + + c_L = cfd%config%wave_speed + c_R = cfd%config%wave_speed + + do i = 1, cfd%domain%mesh%nnodes + u_L = q_face_left(i) + u_R = q_face_right(i) + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux(i) = 0.5_dp * (F_L + F_R) - 0.5_dp * Smax * (u_R - u_L) + end do + end subroutine rusanov_flux + + ! Engquist-Osher flux + subroutine engquist_osher_flux(q_face_left, q_face_right, flux, cfd) + real(dp), intent(in) :: q_face_left(:), q_face_right(:) + real(dp), intent(out) :: flux(:) + type(CfdType), intent(in) :: cfd + + integer :: i + real(dp) :: c, cp, cm, u_L, u_R + + c = cfd%config%wave_speed + + do i = 1, cfd%domain%mesh%nnodes + cp = 0.5_dp * (c + abs(c)) + cm = 0.5_dp * (c - abs(c)) + u_L = q_face_left(i) + u_R = q_face_right(i) + flux(i) = cp * u_L + cm * u_R + end do + end subroutine engquist_osher_flux + + ! Inviscid flux selection + subroutine inviscid_flux(q_face_left, q_face_right, flux, cfd) + real(dp), intent(in) :: q_face_left(:), q_face_right(:) + real(dp), intent(out) :: flux(:) + type(CfdType), intent(in) :: cfd + + if (cfd%config%flux_type == 0) then + call rusanov_flux(q_face_left, q_face_right, flux, cfd) + else + call engquist_osher_flux(q_face_left, q_face_right, flux, cfd) + end if + end subroutine inviscid_flux + + ! =================================================================== + ! Residual Computation + ! =================================================================== + + ! Compute residual (flux divergence) - CORRECTED VERSION + subroutine residual(q, cfd) + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + integer :: i + + ! Apply boundary conditions first + call boundary(cfd%solution%u, cfd) + + ! Reconstruction + call reconstruction(q, cfd) + + ! Compute fluxes + call inviscid_flux(cfd%solution%q_face_left, cfd%solution%q_face_right, & + cfd%solution%flux, cfd) + + ! Compute residual - corrected indexing + do i = 1, cfd%domain%mesh%ncells + cfd%solution%res(i) = -(cfd%solution%flux(i+1) - cfd%solution%flux(i)) / & + cfd%domain%mesh%dx + end do + end subroutine residual + + ! =================================================================== + ! Time Integration + ! =================================================================== + + ! Update old field + subroutine update_oldfield(qn, q, n) + real(dp), intent(out) :: qn(:) + real(dp), intent(in) :: q(:) + integer, intent(in) :: n + + qn(1:n) = q(1:n) + end subroutine update_oldfield + + ! 1st-order Runge-Kutta (Euler) - SIMPLIFIED + subroutine runge_kutta_1(cfd) + type(CfdType), intent(inout) :: cfd + + integer :: i, j + real(dp) :: dt + + dt = cfd%config%dt + + ! Apply boundary conditions + call boundary(cfd%solution%u, cfd) + + ! Compute residual + call residual(cfd%solution%u, cfd) + + ! Update solution + do i = cfd%domain%ist, cfd%domain%ied - 1 + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = cfd%solution%u(i) + dt * cfd%solution%res(j) + end do + + ! Apply boundary conditions again + call boundary(cfd%solution%u, cfd) + + ! Save old solution + call update_oldfield(cfd%solution%un, cfd%solution%u, cfd%domain%ntcells) + end subroutine runge_kutta_1 + + ! 2nd-order Runge-Kutta (Heun) + subroutine runge_kutta_2(cfd) + type(CfdType), intent(inout) :: cfd + + integer :: i, j + real(dp) :: dt + + dt = cfd%config%dt + + ! Stage 1 + call boundary(cfd%solution%u, cfd) + call residual(cfd%solution%u, cfd) + + do i = cfd%domain%ist, cfd%domain%ied - 1 + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = cfd%solution%u(i) + dt * cfd%solution%res(j) + end do + call boundary(cfd%solution%u, cfd) + + ! Stage 2 + call residual(cfd%solution%u, cfd) + do i = cfd%domain%ist, cfd%domain%ied - 1 + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = 0.5_dp * cfd%solution%un(i) + & + 0.5_dp * cfd%solution%u(i) + & + 0.5_dp * dt * cfd%solution%res(j) + end do + call boundary(cfd%solution%u, cfd) + + call update_oldfield(cfd%solution%un, cfd%solution%u, cfd%domain%ntcells) + end subroutine runge_kutta_2 + + ! Runge-Kutta selection + subroutine runge_kutta(cfd) + type(CfdType), intent(inout) :: cfd + + select case(cfd%config%rk_order) + case(1) + call runge_kutta_1(cfd) + case(2) + call runge_kutta_2(cfd) + case default + call runge_kutta_1(cfd) + end select + end subroutine runge_kutta + + ! =================================================================== + ! Simulation Driver + ! =================================================================== + + ! Run simulation to final time + function run_simulation(cfd, final_time) result(u_result) + type(CfdType), intent(inout) :: cfd + real(dp), intent(in) :: final_time + real(dp), allocatable :: u_result(:) + + real(dp) :: t, dt, dt_old + integer :: step, max_steps + + allocate(u_result(cfd%domain%mesh%ncells)) + + t = 0.0_dp + dt_old = cfd%config%dt + dt = dt_old + max_steps = 10000 ! Safety limit + + print *, "Starting time integration..." + print *, " Final time: ", final_time + print *, " Time step: ", dt + print *, " CFL number: ", cfd%config%cfl + + step = 0 + do while (t < final_time - 1.0e-12_dp .and. step < max_steps) + step = step + 1 + + if (t + dt > final_time) then + dt = final_time - t + end if + + cfd%config%dt = dt + call runge_kutta(cfd) + t = t + dt + + ! Progress report + if (mod(step, 100) == 0) then + print *, " Step ", step, ", Time = ", t + end if + end do + + if (step >= max_steps) then + print *, "Warning: Reached maximum number of steps (", max_steps, ")" + end if + + cfd%config%dt = dt_old + + print *, "Time integration completed:" + print *, " Total steps: ", step + print *, " Final time: ", t + + ! Extract physical solution (without ghost cells) + u_result = cfd%solution%u(cfd%domain%ist:cfd%domain%ied) + end function run_simulation + + ! =================================================================== + ! Main Analysis Function + ! =================================================================== + + ! Perform ENO-WENO comparative analysis + subroutine performEnoWenoAnalysis() + type(CfdConfigType) :: config_eno3, config_weno3 + type(MeshType) :: mesh + type(ComputationalDomainType) :: domain_eno3, domain_weno3 + type(CfdType) :: cfd_eno3, cfd_weno3 + real(dp), allocatable :: u_eno(:), u_weno(:), u_analytical(:) + real(dp), allocatable :: xcc(:) + integer :: i, ncells, iunit + + ! Initialize mesh + call mesh%init() + ncells = mesh%ncells + allocate(xcc(ncells)) + xcc = mesh%xcc + + print *, "==========================================" + print *, "Mesh parameters:" + print *, " ncells = ", ncells + print *, " dx = ", mesh%dx + print *, " L = ", mesh%L + print *, "==========================================" + + ! Configure ENO3 - using simple 2nd order for stability + config_eno3%recon_scheme = "eno" + config_eno3%spatial_order = 2 ! Use 2nd order for stability + config_eno3%flux_type = 0 + config_eno3%rk_order = 1 + config_eno3%wave_speed = 1.0_dp + config_eno3%final_time = 0.625_dp + config_eno3%cfl = 0.5_dp + config_eno3%dt = 0.001_dp ! Small time step + + ! Configure WENO3 + config_weno3%recon_scheme = "weno" + config_weno3%spatial_order = 3 + config_weno3%flux_type = 0 + config_weno3%rk_order = 1 + config_weno3%wave_speed = 1.0_dp + config_weno3%final_time = 0.625_dp + config_weno3%cfl = 0.3_dp ! More strict CFL for WENO + config_weno3%dt = 0.0005_dp ! Even smaller time step + + ! Create domains + call domain_eno3%init(mesh, config_eno3) + call domain_weno3%init(mesh, config_weno3) + + ! Create CFD solvers + call cfd_eno3%init(config_eno3, domain_eno3) + call cfd_weno3%init(config_weno3, domain_weno3) + + ! Allocate arrays + allocate(u_eno(ncells), u_weno(ncells), u_analytical(ncells)) + + ! Run ENO simulation + print *, "==========================================" + print *, "Running ENO simulation..." + print *, " Scheme: ENO", config_eno3%spatial_order + print *, " Time step: ", config_eno3%dt + print *, "==========================================" + + call init_field(cfd_eno3) + u_eno = run_simulation(cfd_eno3, config_eno3%final_time) + + ! Run WENO simulation + print *, "==========================================" + print *, "Running WENO simulation..." + print *, " Scheme: WENO", config_weno3%spatial_order + print *, " Time step: ", config_weno3%dt + print *, "==========================================" + + call init_field(cfd_weno3) + u_weno = run_simulation(cfd_weno3, config_weno3%final_time) + + ! Compute analytical solution + print *, "Computing analytical solution..." + do i = 1, ncells + u_analytical(i) = analytical_solution(xcc(i), config_weno3%final_time, & + config_weno3%wave_speed, mesh%L) + end do + + ! Write results to files + print *, "Writing results to files..." + + ! Write ENO results + open(newunit=iunit, file='eno_results.txt', status='replace') + write(iunit, '(A)') '# x, u (ENO)' + do i = 1, ncells + write(iunit, '(2F12.6)') xcc(i), u_eno(i) + end do + close(iunit) + + ! Write WENO results + open(newunit=iunit, file='weno_results.txt', status='replace') + write(iunit, '(A)') '# x, u (WENO)' + do i = 1, ncells + write(iunit, '(2F12.6)') xcc(i), u_weno(i) + end do + close(iunit) + + ! Write analytical results + open(newunit=iunit, file='analytical_results.txt', status='replace') + write(iunit, '(A)') '# x, u (Analytical)' + do i = 1, ncells + write(iunit, '(2F12.6)') xcc(i), u_analytical(i) + end do + close(iunit) + + ! Print some statistics + print *, "==========================================" + print *, "Simulation statistics:" + print *, " ENO min/max: ", minval(u_eno), maxval(u_eno) + print *, " WENO min/max: ", minval(u_weno), maxval(u_weno) + print *, " Analytical min/max: ", minval(u_analytical), maxval(u_analytical) + print *, "==========================================" + + print *, "Simulation completed successfully!" + print *, "Results written to:" + print *, " eno_results.txt" + print *, " weno_results.txt" + print *, " analytical_results.txt" + print *, "" + print *, "To generate the comparison plot, run:" + print *, " python postprocess.py" + print *, "==========================================" + + deallocate(u_eno, u_weno, u_analytical, xcc) + end subroutine performEnoWenoAnalysis + +end module cfd_solver + +! =================================================================== +! Main Program +! =================================================================== +program main + use cfd_solver + implicit none + + print *, "==========================================" + print *, "OneFLOW-CFD Solver for 1D Convection" + print *, "ENO vs WENO Comparison" + print *, "==========================================" + + call performEnoWenoAnalysis() + + print *, "Program finished successfully!" + +end program main \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01e/postprocess.py b/example/1d-linear-convection/weno3/fortran/cfd/01e/postprocess.py new file mode 100644 index 00000000..489212dd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01e/postprocess.py @@ -0,0 +1,52 @@ +import numpy as np +import matplotlib.pyplot as plt + +def read_results(filename): + """Read results from Fortran output file""" + try: + data = np.loadtxt(filename, comments='#') + return data[:, 0], data[:, 1] + except Exception as e: + print(f"Error reading {filename}: {e}") + return np.array([]), np.array([]) + +def main(): + print("Reading Fortran output files...") + + # Read all data files + x_eno, u_eno = read_results('eno_results.txt') + x_weno, u_weno = read_results('weno_results.txt') + x_analytical, u_analytical = read_results('analytical_results.txt') + + # Check if we have data + if len(x_eno) == 0 or len(x_weno) == 0 or len(x_analytical) == 0: + print("Error: Could not read all data files.") + print("Make sure to run the Fortran program first.") + return + + # Create plot + plt.figure(figsize=(10, 6)) + + # Plot results + plt.plot(x_eno, u_eno, 'bo-', linewidth=1, markersize=3, + markerfacecolor='none', label='ENO3') + plt.plot(x_weno, u_weno, 'gs-', linewidth=1, markersize=3, + markerfacecolor='none', label='WENO3') + plt.plot(x_analytical, u_analytical, 'r-', linewidth=2, label='Analytical') + + # Customize plot + plt.title('1D Convection: ENO3 vs WENO3 (t=0.625)') + plt.xlabel('x') + plt.ylabel('u') + plt.legend() + plt.grid(True, alpha=0.3) + + # Save and show + plt.tight_layout() + plt.savefig('comparison.png', dpi=150) + plt.show() + + print("Plot saved as comparison.png") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01f/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/cfd/01f/CMakeLists.txt new file mode 100644 index 00000000..653a6751 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01f/CMakeLists.txt @@ -0,0 +1,22 @@ +# CMakeLists.txt +cmake_minimum_required(VERSION 4.2.1) # 更高版本更好支持Fortran + +project(OneFLOW_CFD LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +# 按依赖顺序显式列出源文件(解决 Intel + VS 编译顺序问题) +set(SOURCES + cfd_solver.f90 +) + +add_executable(oneflow_cfd ${SOURCES}) + +# 包含模块目录(解决 .mod 未找到) +target_include_directories(oneflow_cfd PRIVATE ${CMAKE_Fortran_MODULE_DIRECTORY}) diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01f/README.md b/example/1d-linear-convection/weno3/fortran/cfd/01f/README.md new file mode 100644 index 00000000..95888ac2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01f/README.md @@ -0,0 +1,26 @@ +# OneFLOW-CFD: 1D Convection Solver + +A Fortran implementation of 1D linear convection equation solver with ENO/WENO reconstruction schemes comparison. + +## Features + +- **Numerical Schemes**: ENO3 and WENO3 reconstruction +- **Time Integration**: Runge-Kutta methods (RK1, RK2) +- **Flux Schemes**: Rusanov and Engquist-Osher fluxes +- **Boundary Conditions**: Periodic boundary conditions +- **Visualization**: Python-based plotting and analysis +- **Automation**: Complete build-run-plot pipeline + +## Quick Start + +### Prerequisites +- Fortran compiler (Intel ifx/ifort, GNU gfortran, or LLVM flang) +- CMake 3.10+ +- Python 3.8+ with NumPy and Matplotlib + +### Installation + +1. Clone or download the project: + ```bash + git clone + cd OneFLOW-CFD-Fortran \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01f/cfd_solver.f90 b/example/1d-linear-convection/weno3/fortran/cfd/01f/cfd_solver.f90 new file mode 100644 index 00000000..34cf0c42 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01f/cfd_solver.f90 @@ -0,0 +1,943 @@ +! OneFLOW-CFD Solver for 1D Convection Equation +! ENO/WENO Reconstruction Comparison - Single File Implementation + +module cfd_solver + use, intrinsic :: iso_fortran_env, only: dp => real64 + implicit none + + private + + ! =================================================================== + ! Forward Type Declarations + ! =================================================================== + type, public :: CfdConfigType + character(len=10) :: recon_scheme = "eno" + integer :: flux_type = 0 + integer :: rk_order = 1 + integer :: spatial_order = 3 + real(dp) :: wave_speed = 1.0_dp + real(dp) :: final_time = 0.625_dp + real(dp) :: dt = 0.025_dp ! 减小时间步长 + real(dp) :: cfl = 0.5_dp ! CFL数 + end type CfdConfigType + + type, public :: MeshType + real(dp) :: xmin = 0.0_dp + real(dp) :: xmax = 2.0_dp + integer :: ncells = 40 + integer :: nnodes = 0 + integer :: nx = 0 + real(dp) :: L = 0.0_dp + real(dp) :: dx = 0.0_dp + real(dp), allocatable :: x(:), xcc(:) + contains + procedure :: init => mesh_init + end type MeshType + + type, public :: ComputationalDomainType + type(MeshType) :: mesh + type(CfdConfigType) :: config + integer :: nghosts = 0 + integer :: ist = 0 + integer :: ied = 0 + integer :: ntcells = 0 + contains + procedure :: init => domain_init + end type ComputationalDomainType + + type, public :: SolutionType + type(ComputationalDomainType) :: domain + real(dp), allocatable :: q_face_left(:), q_face_right(:) + real(dp), allocatable :: flux(:), res(:) + real(dp), allocatable :: u(:), un(:) + contains + procedure :: init => solution_init + end type SolutionType + + ! Main CFD solver class + type, public :: CfdType + type(CfdConfigType) :: config + type(ComputationalDomainType) :: domain + type(SolutionType) :: solution + class(*), allocatable :: reconstructor + contains + procedure :: init => cfd_init + end type CfdType + + ! Abstract reconstructor base class + type, abstract, public :: ReconstructorType + contains + procedure(reconstruct_interface), deferred, pass :: reconstruct + end type ReconstructorType + + ! Define abstract interface + abstract interface + subroutine reconstruct_interface(this, q, cfd) + import :: ReconstructorType, CfdType, dp + class(ReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + end subroutine reconstruct_interface + end interface + + ! ENO reconstructor + type, extends(ReconstructorType), public :: EnoReconstructorType + integer :: spatial_order + integer :: ntcells + integer, allocatable :: lmc(:) + real(dp), allocatable :: coef(:,:) + real(dp), allocatable :: dd(:,:) + contains + procedure :: reconstruct => eno_reconstruct + procedure :: init => eno_init + end type EnoReconstructorType + + ! WENO reconstructor + type, extends(ReconstructorType), public :: WenoReconstructorType + contains + procedure :: reconstruct => weno_reconstruct + end type WenoReconstructorType + + ! =================================================================== + ! Public Procedures + ! =================================================================== + public :: run_simulation, init_field, analytical_solution, performEnoWenoAnalysis + + ! =================================================================== + ! Module Variables + ! =================================================================== + real(dp), parameter :: eps_weno = 1.0e-6_dp + +contains + + ! =================================================================== + ! Initialization Methods + ! =================================================================== + + ! Mesh initialization + subroutine mesh_init(this) + class(MeshType), intent(inout) :: this + integer :: i + + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, dp) + + allocate(this%x(this%nnodes), this%xcc(this%ncells)) + + ! Node coordinates + do i = 1, this%nnodes + this%x(i) = this%xmin + (i-1) * this%dx + end do + + ! Cell center coordinates + do i = 1, this%ncells + this%xcc(i) = 0.5_dp * (this%x(i) + this%x(i+1)) + end do + end subroutine mesh_init + + ! Domain initialization + subroutine domain_init(this, mesh, config) + class(ComputationalDomainType), intent(inout) :: this + type(MeshType), intent(in) :: mesh + type(CfdConfigType), intent(in) :: config + + this%mesh = mesh + this%config = config + + ! Calculate ghost cells + if (trim(config%recon_scheme) == "eno") then + this%nghosts = config%spatial_order + else if (trim(config%recon_scheme) == "weno") then + this%nghosts = config%spatial_order / 2 + 1 + else + error stop "Unknown reconstruction scheme" + end if + + this%ist = this%nghosts + 1 + this%ied = this%ist + mesh%ncells - 1 + this%ntcells = mesh%ncells + 2 * this%nghosts + + print *, "Domain initialized:" + print *, " mesh.ncells = ", mesh%ncells + print *, " spatial_order = ", config%spatial_order + print *, " nghosts = ", this%nghosts + print *, " ist = ", this%ist, ", ied = ", this%ied + print *, " dx = ", mesh%dx + end subroutine domain_init + + ! Solution initialization + subroutine solution_init(this, domain) + class(SolutionType), intent(inout) :: this + type(ComputationalDomainType), intent(in) :: domain + + this%domain = domain + + allocate(this%q_face_left(domain%mesh%nnodes)) + allocate(this%q_face_right(domain%mesh%nnodes)) + allocate(this%flux(domain%mesh%nnodes)) + allocate(this%res(domain%mesh%ncells)) + allocate(this%u(domain%ntcells)) + allocate(this%un(domain%ntcells)) + + this%q_face_left = 0.0_dp + this%q_face_right = 0.0_dp + this%flux = 0.0_dp + this%res = 0.0_dp + this%u = 0.0_dp + this%un = 0.0_dp + end subroutine solution_init + + ! ENO reconstructor initialization + subroutine eno_init(this, spatial_order, ntcells) + class(EnoReconstructorType), intent(inout) :: this + integer, intent(in) :: spatial_order + integer, intent(in) :: ntcells + + this%spatial_order = spatial_order + this%ntcells = ntcells + + allocate(this%lmc(ntcells)) + allocate(this%coef(spatial_order+1, spatial_order)) + allocate(this%dd(spatial_order, ntcells)) + + this%lmc = 0 + this%coef = 0.0_dp + this%dd = 0.0_dp + + ! Initialize coefficients + call init_coef(spatial_order, this%coef) + end subroutine eno_init + + ! CFD solver initialization + subroutine cfd_init(this, config, domain) + class(CfdType), intent(inout) :: this + type(CfdConfigType), intent(in) :: config + type(ComputationalDomainType), intent(in) :: domain + + print *, "CFD solver initialization:" + + this%config = config + this%domain = domain + call this%solution%init(domain) + + ! Create reconstructor based on scheme + if (trim(config%recon_scheme) == "eno") then + allocate(EnoReconstructorType :: this%reconstructor) + select type(rec => this%reconstructor) + type is (EnoReconstructorType) + call rec%init(config%spatial_order, domain%ntcells) + end select + else if (trim(config%recon_scheme) == "weno") then + allocate(WenoReconstructorType :: this%reconstructor) + else + error stop "Unknown reconstruction scheme" + end if + + ! Adjust time step based on CFL condition + call calculate_dt(this) + end subroutine cfd_init + + ! Calculate time step based on CFL condition + subroutine calculate_dt(cfd) + type(CfdType), intent(inout) :: cfd + + real(dp) :: dt_cfl + + ! CFL condition: dt <= CFL * dx / |wave_speed| + dt_cfl = cfd%config%cfl * cfd%domain%mesh%dx / abs(cfd%config%wave_speed) + + print *, " cfd%config%dt = ", cfd%config%dt + print *, " dt_cfl = ", dt_cfl + + if (cfd%config%dt > dt_cfl) then + print *, "Adjusting time step for stability:" + print *, " Original dt = ", cfd%config%dt + print *, " CFL dt = ", dt_cfl + cfd%config%dt = dt_cfl + print *, " Using dt = ", cfd%config%dt + end if + + print *, " calculate_dt cfd%config%dt = ", cfd%config%dt + end subroutine calculate_dt + + ! =================================================================== + ! Initial Conditions and Analytical Solution + ! =================================================================== + + ! Initial condition: step function + function initial_condition(x) result(u0) + real(dp), intent(in) :: x + real(dp) :: u0 + + if (0.5_dp <= x .and. x <= 1.0_dp) then + u0 = 2.0_dp + else + u0 = 1.0_dp + end if + end function initial_condition + + ! Analytical solution with periodic BC + function analytical_solution(x, t, a, L) result(u) + real(dp), intent(in) :: x, t, a, L + real(dp) :: u, x_shifted + + x_shifted = mod(x - a * t + L, L) + u = initial_condition(x_shifted) + end function analytical_solution + + ! Initialize field with step function + subroutine init_field(cfd) + type(CfdType), intent(inout) :: cfd + integer :: i, j + + do i = 1, cfd%domain%mesh%ncells + if (0.5_dp <= cfd%domain%mesh%xcc(i) .and. cfd%domain%mesh%xcc(i) <= 1.0_dp) then + cfd%solution%u(cfd%domain%ist + i - 1) = 2.0_dp + else + cfd%solution%u(cfd%domain%ist + i - 1) = 1.0_dp + end if + end do + + call boundary(cfd%solution%u, cfd) + call update_oldfield(cfd%solution%un, cfd%solution%u, cfd%domain%ntcells) + end subroutine init_field + + ! =================================================================== + ! Boundary Conditions + ! =================================================================== + + ! Periodic boundary conditions - CORRECTED VERSION + subroutine periodic_boundary(u, cfd) + real(dp), intent(inout) :: u(:) + type(CfdType), intent(in) :: cfd + integer :: i + + ! Copy interior cells to ghost cells + ! Left ghost cells = right interior cells + do i = 1, cfd%domain%nghosts + u(cfd%domain%ist - i) = u(cfd%domain%ied - cfd%domain%nghosts + i - 1) + end do + + ! Right ghost cells = left interior cells + do i = 1, cfd%domain%nghosts + u(cfd%domain%ied + i) = u(cfd%domain%ist + i - 1) + end do + end subroutine periodic_boundary + + ! Boundary condition wrapper + subroutine boundary(u, cfd) + real(dp), intent(inout) :: u(:) + type(CfdType), intent(in) :: cfd + + call periodic_boundary(u, cfd) + end subroutine boundary + + ! =================================================================== + ! Reconstruction Methods + ! =================================================================== + + ! Initialize ENO/WENO coefficients + subroutine init_coef(spatial_order, coef) + integer, intent(in) :: spatial_order + real(dp), intent(out) :: coef(:,:) + + coef = 0.0_dp + + select case(spatial_order) + case(1) + coef(1,1) = 1.0_dp + coef(2,1) = 1.0_dp + + case(2) + coef(1,1:2) = [ 3.0_dp/2.0_dp, -1.0_dp/2.0_dp ] + coef(2,1:2) = [ 1.0_dp/2.0_dp, 1.0_dp/2.0_dp ] + coef(3,1:2) = [ -1.0_dp/2.0_dp, 3.0_dp/2.0_dp ] + + case(3) + coef(1,1:3) = [ 11.0_dp/6.0_dp, -7.0_dp/6.0_dp, 1.0_dp/3.0_dp ] + coef(2,1:3) = [ 1.0_dp/3.0_dp, 5.0_dp/6.0_dp, -1.0_dp/6.0_dp ] + coef(3,1:3) = [ -1.0_dp/6.0_dp, 5.0_dp/6.0_dp, 1.0_dp/3.0_dp ] + coef(4,1:3) = [ 1.0_dp/3.0_dp, -7.0_dp/6.0_dp, 11.0_dp/6.0_dp ] + + case default + error stop "Unsupported spatial order" + end select + end subroutine init_coef + + subroutine first_order_reconstruct(this, q, cfd) + class(EnoReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + integer :: i, j + integer :: nghosts, ist, ied + + nghosts = cfd%domain%nghosts + ist = cfd%domain%ist + ied = cfd%domain%ied + + ! For now, use simple 2nd order reconstruction + do i = ist, ied + j = i - ist + 1 ! 1-based index for interfaces + + cfd%solution%q_face_left(j) = q(i-1) + cfd%solution%q_face_right(j) = q(i) + end do + end subroutine first_order_reconstruct + + subroutine eno_reconstruct(this, q, cfd) + class(EnoReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + type(ComputationalDomainType), pointer :: domain + + integer :: i, j, m, k1, k2, r1, r2 + integer :: nghosts, ist, ied + + nghosts = cfd%domain%nghosts + ist = cfd%domain%ist + ied = cfd%domain%ied + + ! 1. 差商计算 (dd[1,:] = q) + this%dd(1, :) = q(:) + do m = 2, this%spatial_order + do j = 1, cfd%domain%ntcells - m + 1 + this%dd(m, j) = this%dd(m-1, j+1) - this%dd(m-1, j) + end do + end do + + ! 2. 选择 smoothest stencil + do i = ist - 1, ied ! Python: range(ist-1, ied+1) → ied+1-1 = ied + this%lmc(i) = i + do m = 2, this%spatial_order + if ( abs(this%dd(m, this%lmc(i) - 1) ) < abs(this%dd(m, this%lmc(i)))) then + this%lmc(i) = this%lmc(i) - 1 + end if + end do + end do + + associate ( & + q_face_left => cfd%solution%q_face_left, & + q_face_right => cfd%solution%q_face_right & + ) + ! 这里可以直接使用 q_face_left 和 q_face_right + ! 3. 重构界面值 + do i = ist, ied + j = i - ist + 1 ! 1-based index for interfaces + k1 = this%lmc(i - 1) + k2 = this%lmc(i) + r1 = (i - 1) - k1 + 1 + r2 = i - k2 + 1 + q_face_left(j) = 0.0 + q_face_right(j) = 0.0 + do m = 1, this%spatial_order + q_face_left(j) = q_face_left(j) + q(k1 + m - 1) * this%coef(r1 + 1, m) + q_face_right(j) = q_face_right(j) + q(k2 + m - 1) * this%coef(r2, m) + end do + end do + end associate + + end subroutine eno_reconstruct + + ! WENO-3 nonlinear weights for left-biased stencil + function wc3L(v1, v2, v3) result(f) + real(dp), intent(in) :: v1, v2, v3 + real(dp) :: f, s0, s1, d0, d1, c0, c1, w0, w1, q0, q1 + + ! Smoothness indicators + s0 = (v3 - v2)**2 + s1 = (v2 - v1)**2 + + ! Nonlinear weights + d0 = 2.0_dp/3.0_dp + d1 = 1.0_dp/3.0_dp + + c0 = d0 / ((eps_weno + s0)**2) + c1 = d1 / ((eps_weno + s1)**2) + + w0 = c0 / (c0 + c1) + w1 = c1 / (c0 + c1) + + ! Candidate stencils + q0 = 0.5_dp * v2 + 0.5_dp * v3 + q1 = -0.5_dp * v1 + 1.5_dp * v2 + + ! Reconstructed value + f = w0 * q0 + w1 * q1 + end function wc3L + + ! WENO-3 nonlinear weights for right-biased stencil + function wc3R(v1, v2, v3) result(f) + real(dp), intent(in) :: v1, v2, v3 + real(dp) :: f, s0, s1, d0, d1, c0, c1, w0, w1, q0, q1 + + ! Smoothness indicators + s0 = (v2 - v1)**2 + s1 = (v3 - v2)**2 + + ! Nonlinear weights + d0 = 2.0_dp/3.0_dp + d1 = 1.0_dp/3.0_dp + + c0 = d0 / ((eps_weno + s0)**2) + c1 = d1 / ((eps_weno + s1)**2) + + w0 = c0 / (c0 + c1) + w1 = c1 / (c0 + c1) + + ! Candidate stencils + q0 = 0.5_dp * v1 + 0.5_dp * v2 + q1 = 1.5_dp * v2 - 0.5_dp * v3 + + ! Reconstructed value + f = w0 * q0 + w1 * q1 + end function wc3R + + ! WENO-3 reconstruction for left interface + subroutine weno3L_periodic(cfd, u, qL) + type(CfdType), intent(in) :: cfd + real(dp), intent(in) :: u(:) + real(dp), intent(out) :: qL(:) + + integer :: i, j, nghosts, ist, ied + + nghosts = cfd%domain%nghosts + ist = cfd%domain%ist + ied = cfd%domain%ied + + do i = ist-1, ied-1 + j = i - (ist - 1) + 1 + qL(j) = wc3L(u(i-1), u(i), u(i+1)) + end do + + end subroutine weno3L_periodic + + ! WENO-3 reconstruction for right interface + subroutine weno3R_periodic(cfd, u, qR) + type(CfdType), intent(in) :: cfd + real(dp), intent(in) :: u(:) + real(dp), intent(out) :: qR(:) + + integer :: i, j, nghosts, ist, ied + + nghosts = cfd%domain%nghosts + ist = cfd%domain%ist + ied = cfd%domain%ied + + do i = ist, ied + j = i - ist + 1 + qR(j) = wc3R(u(i-1), u(i), u(i+1)) + end do + + end subroutine weno3R_periodic + + ! WENO reconstruction + subroutine weno_reconstruct(this, q, cfd) + class(WenoReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + call weno3L_periodic(cfd, q, cfd%solution%q_face_left) + call weno3R_periodic(cfd, q, cfd%solution%q_face_right) + end subroutine weno_reconstruct + + ! General reconstruction wrapper + subroutine reconstruction(q, cfd) + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + select type(rec => cfd%reconstructor) + type is (EnoReconstructorType) + call rec%reconstruct(q, cfd) + type is (WenoReconstructorType) + call rec%reconstruct(q, cfd) + class default + error stop "Unknown reconstructor type" + end select + end subroutine reconstruction + + ! =================================================================== + ! Flux Functions + ! =================================================================== + + ! Rusanov flux + subroutine rusanov_flux(q_face_left, q_face_right, flux, cfd) + real(dp), intent(in) :: q_face_left(:), q_face_right(:) + real(dp), intent(out) :: flux(:) + type(CfdType), intent(in) :: cfd + + integer :: i + real(dp) :: u_L, u_R, F_L, F_R, c_L, c_R, Smax + + c_L = cfd%config%wave_speed + c_R = cfd%config%wave_speed + + do i = 1, cfd%domain%mesh%nnodes + u_L = q_face_left(i) + u_R = q_face_right(i) + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux(i) = 0.5_dp * (F_L + F_R) - 0.5_dp * Smax * (u_R - u_L) + end do + end subroutine rusanov_flux + + ! Engquist-Osher flux + subroutine engquist_osher_flux(q_face_left, q_face_right, flux, cfd) + real(dp), intent(in) :: q_face_left(:), q_face_right(:) + real(dp), intent(out) :: flux(:) + type(CfdType), intent(in) :: cfd + + integer :: i + real(dp) :: c, cp, cm, u_L, u_R + + c = cfd%config%wave_speed + + do i = 1, cfd%domain%mesh%nnodes + cp = 0.5_dp * (c + abs(c)) + cm = 0.5_dp * (c - abs(c)) + u_L = q_face_left(i) + u_R = q_face_right(i) + flux(i) = cp * u_L + cm * u_R + end do + end subroutine engquist_osher_flux + + ! Inviscid flux selection + subroutine inviscid_flux(q_face_left, q_face_right, flux, cfd) + real(dp), intent(in) :: q_face_left(:), q_face_right(:) + real(dp), intent(out) :: flux(:) + type(CfdType), intent(in) :: cfd + + if (cfd%config%flux_type == 0) then + call rusanov_flux(q_face_left, q_face_right, flux, cfd) + else + call engquist_osher_flux(q_face_left, q_face_right, flux, cfd) + end if + end subroutine inviscid_flux + + ! =================================================================== + ! Residual Computation + ! =================================================================== + + ! Compute residual (flux divergence) - CORRECTED VERSION + subroutine residual(q, cfd) + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + integer :: i + + ! Apply boundary conditions first + call boundary(cfd%solution%u, cfd) + + ! Reconstruction + call reconstruction(q, cfd) + + ! Compute fluxes + call inviscid_flux(cfd%solution%q_face_left, cfd%solution%q_face_right, & + cfd%solution%flux, cfd) + + ! Compute residual - corrected indexing + do i = 1, cfd%domain%mesh%ncells + cfd%solution%res(i) = -(cfd%solution%flux(i+1) - cfd%solution%flux(i)) / & + cfd%domain%mesh%dx + end do + end subroutine residual + + ! =================================================================== + ! Time Integration + ! =================================================================== + + ! Update old field + subroutine update_oldfield(qn, q, n) + real(dp), intent(out) :: qn(:) + real(dp), intent(in) :: q(:) + integer, intent(in) :: n + + qn(1:n) = q(1:n) + end subroutine update_oldfield + + ! 1st-order Runge-Kutta (Euler) - SIMPLIFIED + subroutine runge_kutta_1(cfd) + type(CfdType), intent(inout) :: cfd + + integer :: i, j + real(dp) :: dt + + dt = cfd%config%dt + + ! Apply boundary conditions + call boundary(cfd%solution%u, cfd) + + ! Compute residual + call residual(cfd%solution%u, cfd) + + ! Update solution + do i = cfd%domain%ist, cfd%domain%ied - 1 + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = cfd%solution%u(i) + dt * cfd%solution%res(j) + end do + + ! Apply boundary conditions again + call boundary(cfd%solution%u, cfd) + + ! Save old solution + call update_oldfield(cfd%solution%un, cfd%solution%u, cfd%domain%ntcells) + end subroutine runge_kutta_1 + + ! 2nd-order Runge-Kutta (Heun) + subroutine runge_kutta_2(cfd) + type(CfdType), intent(inout) :: cfd + + integer :: i, j + real(dp) :: dt + + dt = cfd%config%dt + + ! Stage 1 + call boundary(cfd%solution%u, cfd) + call residual(cfd%solution%u, cfd) + + do i = cfd%domain%ist, cfd%domain%ied - 1 + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = cfd%solution%u(i) + dt * cfd%solution%res(j) + end do + call boundary(cfd%solution%u, cfd) + + ! Stage 2 + call residual(cfd%solution%u, cfd) + do i = cfd%domain%ist, cfd%domain%ied - 1 + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = 0.5_dp * cfd%solution%un(i) + & + 0.5_dp * cfd%solution%u(i) + & + 0.5_dp * dt * cfd%solution%res(j) + end do + call boundary(cfd%solution%u, cfd) + + call update_oldfield(cfd%solution%un, cfd%solution%u, cfd%domain%ntcells) + end subroutine runge_kutta_2 + + ! Runge-Kutta selection + subroutine runge_kutta(cfd) + type(CfdType), intent(inout) :: cfd + + select case(cfd%config%rk_order) + case(1) + call runge_kutta_1(cfd) + case(2) + call runge_kutta_2(cfd) + case default + call runge_kutta_1(cfd) + end select + end subroutine runge_kutta + + ! =================================================================== + ! Simulation Driver + ! =================================================================== + + ! Run simulation to final time + function run_simulation(cfd, final_time) result(u_result) + type(CfdType), intent(inout) :: cfd + real(dp), intent(in) :: final_time + real(dp), allocatable :: u_result(:) + + real(dp) :: t, dt, dt_old + integer :: step, max_steps + + allocate(u_result(cfd%domain%mesh%ncells)) + + t = 0.0_dp + dt_old = cfd%config%dt + dt = dt_old + max_steps = 10000 ! Safety limit + + print *, "Starting time integration..." + print *, " Final time: ", final_time + print *, " Time step: ", dt + print *, " CFL number: ", cfd%config%cfl + + step = 0 + do while (t < final_time - 1.0e-12_dp .and. step < max_steps) + step = step + 1 + + if (t + dt > final_time) then + dt = final_time - t + end if + + cfd%config%dt = dt + call runge_kutta(cfd) + t = t + dt + + ! Progress report + if (mod(step, 100) == 0) then + print *, " Step ", step, ", Time = ", t + end if + end do + + if (step >= max_steps) then + print *, "Warning: Reached maximum number of steps (", max_steps, ")" + end if + + cfd%config%dt = dt_old + + print *, "Time integration completed:" + print *, " Total steps: ", step + print *, " Final time: ", t + + ! Extract physical solution (without ghost cells) + u_result = cfd%solution%u(cfd%domain%ist:cfd%domain%ied) + end function run_simulation + + ! =================================================================== + ! Main Analysis Function + ! =================================================================== + + ! Perform ENO-WENO comparative analysis + subroutine performEnoWenoAnalysis() + type(CfdConfigType) :: config_eno3, config_weno3 + type(MeshType) :: mesh + type(ComputationalDomainType) :: domain_eno3, domain_weno3 + type(CfdType) :: cfd_eno3, cfd_weno3 + real(dp), allocatable :: u_eno(:), u_weno(:), u_analytical(:) + real(dp), allocatable :: xcc(:) + integer :: i, ncells, iunit + + ! Initialize mesh + call mesh%init() + ncells = mesh%ncells + allocate(xcc(ncells)) + xcc = mesh%xcc + + print *, "==========================================" + print *, "Mesh parameters:" + print *, " ncells = ", ncells + print *, " dx = ", mesh%dx + print *, " L = ", mesh%L + print *, "==========================================" + + ! Configure ENO3 - using simple 2nd order for stability + config_eno3%recon_scheme = "eno" + config_eno3%spatial_order = 3 + config_eno3%flux_type = 0 + config_eno3%rk_order = 1 + config_eno3%wave_speed = 1.0_dp + config_eno3%final_time = 0.625_dp + config_eno3%cfl = 1.0_dp + config_eno3%dt = 0.0025_dp + + ! Configure WENO3 + config_weno3%recon_scheme = "weno" + config_weno3%spatial_order = 3 + config_weno3%flux_type = 0 + config_weno3%rk_order = 1 + config_weno3%wave_speed = 1.0_dp + config_weno3%final_time = 0.625_dp + config_weno3%cfl = 1.0_dp + config_weno3%dt = 0.0025_dp + + ! Create domains + call domain_eno3%init(mesh, config_eno3) + call domain_weno3%init(mesh, config_weno3) + + ! Create CFD solvers + call cfd_eno3%init(config_eno3, domain_eno3) + call cfd_weno3%init(config_weno3, domain_weno3) + + ! Allocate arrays + allocate(u_eno(ncells), u_weno(ncells), u_analytical(ncells)) + + ! Run ENO simulation + print *, "==========================================" + print *, "Running ENO simulation..." + print *, " Scheme: ENO", config_eno3%spatial_order + print *, " Time step: ", config_eno3%dt + print *, "==========================================" + + call init_field(cfd_eno3) + u_eno = run_simulation(cfd_eno3, config_eno3%final_time) + + ! Run WENO simulation + print *, "==========================================" + print *, "Running WENO simulation..." + print *, " Scheme: WENO", config_weno3%spatial_order + print *, " Time step: ", config_weno3%dt + print *, "==========================================" + + call init_field(cfd_weno3) + u_weno = run_simulation(cfd_weno3, config_weno3%final_time) + + ! Compute analytical solution + print *, "Computing analytical solution..." + do i = 1, ncells + u_analytical(i) = analytical_solution(xcc(i), config_weno3%final_time, & + config_weno3%wave_speed, mesh%L) + end do + + ! Write results to files + print *, "Writing results to files..." + + ! Write ENO results + open(newunit=iunit, file='eno_results.txt', status='replace') + write(iunit, '(A)') '# x, u (ENO)' + do i = 1, ncells + write(iunit, '(2F12.6)') xcc(i), u_eno(i) + end do + close(iunit) + + ! Write WENO results + open(newunit=iunit, file='weno_results.txt', status='replace') + write(iunit, '(A)') '# x, u (WENO)' + do i = 1, ncells + write(iunit, '(2F12.6)') xcc(i), u_weno(i) + end do + close(iunit) + + ! Write analytical results + open(newunit=iunit, file='analytical_results.txt', status='replace') + write(iunit, '(A)') '# x, u (Analytical)' + do i = 1, ncells + write(iunit, '(2F12.6)') xcc(i), u_analytical(i) + end do + close(iunit) + + ! Print some statistics + print *, "==========================================" + print *, "Simulation statistics:" + print *, " ENO min/max: ", minval(u_eno), maxval(u_eno) + print *, " WENO min/max: ", minval(u_weno), maxval(u_weno) + print *, " Analytical min/max: ", minval(u_analytical), maxval(u_analytical) + print *, "==========================================" + + print *, "Simulation completed successfully!" + print *, "Results written to:" + print *, " eno_results.txt" + print *, " weno_results.txt" + print *, " analytical_results.txt" + print *, "" + print *, "To generate the comparison plot, run:" + print *, " python postprocess.py" + print *, "==========================================" + + deallocate(u_eno, u_weno, u_analytical, xcc) + end subroutine performEnoWenoAnalysis + +end module cfd_solver + +! =================================================================== +! Main Program +! =================================================================== +program main + use cfd_solver + implicit none + + print *, "==========================================" + print *, "OneFLOW-CFD Solver for 1D Convection" + print *, "ENO vs WENO Comparison" + print *, "==========================================" + + call performEnoWenoAnalysis() + + print *, "Program finished successfully!" + +end program main \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01f/postprocess.py b/example/1d-linear-convection/weno3/fortran/cfd/01f/postprocess.py new file mode 100644 index 00000000..489212dd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01f/postprocess.py @@ -0,0 +1,52 @@ +import numpy as np +import matplotlib.pyplot as plt + +def read_results(filename): + """Read results from Fortran output file""" + try: + data = np.loadtxt(filename, comments='#') + return data[:, 0], data[:, 1] + except Exception as e: + print(f"Error reading {filename}: {e}") + return np.array([]), np.array([]) + +def main(): + print("Reading Fortran output files...") + + # Read all data files + x_eno, u_eno = read_results('eno_results.txt') + x_weno, u_weno = read_results('weno_results.txt') + x_analytical, u_analytical = read_results('analytical_results.txt') + + # Check if we have data + if len(x_eno) == 0 or len(x_weno) == 0 or len(x_analytical) == 0: + print("Error: Could not read all data files.") + print("Make sure to run the Fortran program first.") + return + + # Create plot + plt.figure(figsize=(10, 6)) + + # Plot results + plt.plot(x_eno, u_eno, 'bo-', linewidth=1, markersize=3, + markerfacecolor='none', label='ENO3') + plt.plot(x_weno, u_weno, 'gs-', linewidth=1, markersize=3, + markerfacecolor='none', label='WENO3') + plt.plot(x_analytical, u_analytical, 'r-', linewidth=2, label='Analytical') + + # Customize plot + plt.title('1D Convection: ENO3 vs WENO3 (t=0.625)') + plt.xlabel('x') + plt.ylabel('u') + plt.legend() + plt.grid(True, alpha=0.3) + + # Save and show + plt.tight_layout() + plt.savefig('comparison.png', dpi=150) + plt.show() + + print("Plot saved as comparison.png") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01f/run_all.py b/example/1d-linear-convection/weno3/fortran/cfd/01f/run_all.py new file mode 100644 index 00000000..44bf7c89 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01f/run_all.py @@ -0,0 +1,482 @@ +#!/usr/bin/env python3 +""" +OneFLOW-CFD Complete Automation System +Author: Your Name +Date: 2024 +Description: Automated build, run, and visualization pipeline +""" + +import os +import sys +import subprocess +import shutil +import time +from pathlib import Path +import argparse + +class CFDAutomation: + """自动化CFD求解器的完整流程""" + + def __init__(self, build_type="Release", verbose=False): + self.project_root = Path.cwd() + self.build_dir = self.project_root / "build" + self.bin_dir = None + self.executable = None + self.build_type = build_type + self.verbose = verbose + self.results_dir = self.project_root / "results" + # 初始化combined_env,避免属性不存在报错 + self.combined_env = os.environ.copy() + + def print_banner(self): + """打印漂亮的横幅""" + print("\n" + "="*70) + print(" OneFLOW-CFD Automation System") + print(" 1D Convection: ENO3 vs WENO3 Comparison") + print("="*70 + "\n") + + def print_step(self, step_num, description): + """打印步骤信息""" + print(f"[{step_num}/7] {description}") + print("-" * 50) + + def check_environment(self): + """检查运行环境""" + self.print_step(2, "Checking Environment") # 调整步骤号,匹配后续流程 + + # 检查Python + try: + import numpy, matplotlib + print("✓ NumPy and Matplotlib are available") + except ImportError as e: + print(f"✗ Missing Python packages: {e}") + print("Installing required packages...") + try: + subprocess.run([sys.executable, "-m", "pip", "install", "numpy", "matplotlib", "-q"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + shell=True) + print("✓ Packages installed") + except Exception as install_e: + print(f"✗ Package installation failed: {install_e}") + sys.exit(1) + + # 检查Fortran编译器 + compilers = ["ifx", "ifort", "gfortran", "flang"] + found = False + for compiler in compilers: + try: + result = subprocess.run([compiler, "--version"], + env=self.combined_env, + capture_output=True, + text=True, + encoding='utf-8', # 替换为utf-8,避免gbk解码失败 + errors='ignore', # 忽略无效字符,兜底报错 + shell=True) + if result.returncode == 0: + print(f"✓ Found Fortran compiler: {compiler}") + found = True + break + except (FileNotFoundError, Exception): + continue + + if not found: + print("⚠ Warning: No Fortran compiler found in PATH") + print(" On Windows, please run from Intel oneAPI command prompt") + print(" On Linux/Mac, install: ifx, gfortran, or flang") + + # 检查CMake + try: + subprocess.run(["cmake", "--version"], + env=self.combined_env, + capture_output=True, + text=True, + encoding='utf-8', # 替换为utf-8 + errors='ignore', # 兜底无效字符 + shell=True) + print("✓ CMake is available") + except FileNotFoundError: + print("✗ CMake not found. Please install CMake 3.10+") + sys.exit(1) + + def setup_intel_environment(self): + """一次性提取setvars.bat配置后的所有环境变量(仅执行一次)""" + self.print_step(1, "Setting up Intel oneAPI Environment") # 明确步骤1 + + cmd_command = r'cmd.exe /C ""C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && set"' + + try: + result = subprocess.run( + cmd_command, + capture_output=True, + text=True, + encoding='gbk', # 保留gbk解析cmd输出 + errors='ignore', # 新增:忽略无法解码的字符,避免报错 + shell=True, + timeout=30 + ) + except Exception as e: + raise Exception(f"提取oneAPI环境失败:{str(e)}") + + if result.returncode != 0: + raise Exception(f"setvars.bat执行失败:{result.stderr}") + + # 解析环境变量(忽略非有效行) + env_dict = {} + for line in result.stdout.splitlines(): + line = line.strip() + if '=' in line and not line.startswith('='): + try: + key, value = line.split('=', 1) + env_dict[key.strip()] = value.strip() + except: + continue # 忽略分割失败的行,避免中断解析 + + oneapi_env = env_dict + + # 合并环境变量(系统原有环境 + oneAPI环境) + self.combined_env = os.environ.copy() + self.combined_env.update(oneapi_env) + + print("✓ Intel oneAPI environment loaded successfully") + return oneapi_env + + def configure_cmake(self): + """配置CMake""" + self.print_step(3, "Configuring with CMake") + + # 清理旧的构建目录 + if self.build_dir.exists(): + print("Cleaning previous build...") + shutil.rmtree(self.build_dir) + + # 创建构建目录 + self.build_dir.mkdir(exist_ok=True) + + # CMake命令 + cmake_cmd = ["cmake", "-S", str(self.project_root), "-B", str(self.build_dir)] + + # 平台特定选项 + if sys.platform == "win32": + cmake_cmd.extend(["-G", "Visual Studio 17 2022", "-A", "x64"]) + cmake_cmd.extend(["-T", "fortran=ifx"]) + else: + cmake_cmd.extend(["-DCMAKE_BUILD_TYPE=" + self.build_type]) + + print(f"Running: {' '.join(cmake_cmd)}") + + try: + # 核心修改:指定编码为utf-8,添加errors='ignore' + result = subprocess.run(cmake_cmd, check=True, + capture_output=not self.verbose, + env=self.combined_env, + text=True, + encoding='utf-8', + errors='ignore', + shell=True) + if self.verbose and result.stdout: + print(result.stdout) + print("✓ CMake configuration successful") + except subprocess.CalledProcessError as e: + print(f"✗ CMake configuration failed:") + if e.stderr: + print(e.stderr) + sys.exit(1) + + def build_project(self): + """构建项目""" + self.print_step(4, "Building Project") + + build_cmd = ["cmake", "--build", str(self.build_dir), "--config", self.build_type] + + if self.verbose: + build_cmd.append("--verbose") + + print(f"Running: {' '.join(build_cmd)}") + + try: + # 核心修改:指定编码为utf-8,添加errors='ignore',解决UnicodeDecodeError + result = subprocess.run(build_cmd, check=True, + capture_output=not self.verbose, + env=self.combined_env, + text=True, + encoding='utf-8', + errors='ignore', + shell=True) + if self.verbose and result.stdout: + print(result.stdout) + print("✓ Build successful") + + # 查找可执行文件 + self._find_executable() + + except subprocess.CalledProcessError as e: + print(f"✗ Build failed:") + if e.stderr: + print(e.stderr) + sys.exit(1) + + def _find_executable(self): + """查找生成的可执行文件""" + search_paths = [] + + if sys.platform == "win32": + search_paths = [ + self.build_dir / "bin" / self.build_type / "oneflow_cfd.exe", + self.build_dir / self.build_type / "oneflow_cfd.exe", + ] + else: + search_paths = [ + self.build_dir / "oneflow_cfd", + self.build_dir / "bin" / "oneflow_cfd", + ] + + for path in search_paths: + if path.exists(): + self.executable = path + self.bin_dir = path.parent + print(f"✓ Found executable: {self.executable}") + return + + print("⚠ Could not find executable automatically") + print(" Searched in:", [str(p) for p in search_paths]) + + def run_simulation(self): + """运行CFD模拟""" + self.print_step(5, "Running CFD Simulation") + + if not self.executable or not self.executable.exists(): + print("✗ Executable not found!") + return False + + # 确保postprocess.py在运行目录 + postprocess_src = self.project_root / "postprocess.py" + if postprocess_src.exists(): + shutil.copy2(postprocess_src, self.bin_dir / "postprocess.py") + + # 运行可执行文件 + print(f"Running: {self.executable.name}") + print("-" * 50) + + try: + original_dir = os.getcwd() + os.chdir(self.bin_dir) + + result = subprocess.run([str(self.executable)], + check=True, + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + env=self.combined_env, + shell=True) + + # 打印输出 + if result.stdout: + lines = result.stdout.strip().split('\n') + for line in lines: + if any(keyword in line.lower() for keyword in + ['error', 'warning', 'failed', 'success']): + if 'error' in line.lower() or 'failed' in line.lower(): + print(f"✗ {line}") + elif 'warning' in line.lower(): + print(f"⚠ {line}") + else: + print(f"✓ {line}") + else: + print(f" {line}") + + if result.stderr: + print("\nStderr output:") + print(result.stderr) + + print("-" * 50) + print("✓ Simulation completed") + + os.chdir(original_dir) + return True + + except subprocess.CalledProcessError as e: + print(f"✗ Simulation failed with exit code {e.returncode}") + if e.stdout: + print("Output:", e.stdout) + os.chdir(original_dir) + return False + + def generate_visualization(self): + """生成可视化结果""" + self.print_step(6, "Generating Visualization") + + original_dir = os.getcwd() + os.chdir(self.bin_dir) + + postprocess_script = self.bin_dir / "postprocess.py" + if not postprocess_script.exists(): + print("✗ postprocess.py not found in output directory") + os.chdir(original_dir) + return False + + print(f"Running: python {postprocess_script.name}") + + try: + result = subprocess.run([sys.executable, str(postprocess_script)], + check=True, + capture_output=True, + text=True, + encoding='utf-8', # 新增:指定utf-8编码 + errors='ignore', # 新增:忽略无效字符 + env=self.combined_env, + shell=True) + + if result.stdout and self.verbose: + print(result.stdout) + + print("✓ Visualization generated") + os.chdir(original_dir) + return True + + except subprocess.CalledProcessError as e: + print(f"✗ Visualization failed: {e}") + os.chdir(original_dir) + return False + + def organize_results(self): + """整理结果文件""" + self.print_step(7, "Organizing Results") + + # 创建结果目录 + self.results_dir.mkdir(exist_ok=True) + + # 复制结果文件 + result_patterns = ["*results.txt", "comparison.png", "*.dat"] + + for pattern in result_patterns: + for result_file in self.bin_dir.glob(pattern): + if result_file.is_file(): + dest = self.results_dir / result_file.name + # 避免同名文件覆盖提示,直接复制 + shutil.copy2(result_file, dest) + print(f"✓ Copied: {result_file.name}") + + # 创建结果摘要 + self._create_summary() + + print(f"\n✓ Results organized in: {self.results_dir}") + + def _create_summary(self): + """创建结果摘要""" + summary_file = self.results_dir / "simulation_summary.txt" + + with open(summary_file, 'w', encoding='utf-8') as f: # 指定utf-8写入,避免中文乱码 + f.write("OneFLOW-CFD Simulation Summary\n") + f.write("=" * 50 + "\n") + f.write(f"Date: {time.strftime('%Y-%m-%d %H:%M:%S')}\n") + f.write(f"Build Type: {self.build_type}\n") + f.write(f"Platform: {sys.platform}\n") + f.write("\nFiles Generated:\n") + + for file in sorted(self.results_dir.glob("*")): + if file.is_file(): + size_kb = file.stat().st_size / 1024 + f.write(f" - {file.name:20} {size_kb:6.1f} KB\n") + + def run_complete_pipeline(self): + """运行完整管道""" + self.print_banner() + + steps = [ + self.setup_intel_environment, + self.check_environment, + self.configure_cmake, + self.build_project, + self.run_simulation, + self.generate_visualization, + self.organize_results, + ] + + for i, step in enumerate(steps, 1): + try: + step() + except Exception as e: + print(f"\n✗ Step {i} failed: {e}") + print("\nDebug information:") + print(f" Project root: {self.project_root}") + print(f" Build dir: {self.build_dir}") + print(f" Executable: {self.executable}") + sys.exit(1) + + self._print_completion_message() + + def _print_completion_message(self): + """打印完成信息""" + print("\n" + "="*70) + print(" OneFLOW-CFD Automation Complete!") + print("="*70) + print("\nGenerated Files:") + print(" Results Directory: results/") + + for file in sorted(self.results_dir.glob("*")): + if file.is_file(): + print(f" - {file.name}") + + print("\nTo view the plot:") + plot_file = self.results_dir / "comparison.png" + if plot_file.exists(): + if sys.platform == "win32": + print(f" start results/comparison.png") + elif sys.platform == "darwin": + print(f" open results/comparison.png") + else: + print(f" xdg-open results/comparison.png") + + print("\n" + "="*70) + +def main(): + """主函数""" + parser = argparse.ArgumentParser( + description="OneFLOW-CFD Automation System", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + %(prog)s # Run complete pipeline + %(prog)s --verbose # Verbose output + %(prog)s --build Debug # Debug build + %(prog)s --clean # Clean before building + """ + ) + + parser.add_argument("--build", choices=["Release", "Debug", "RelWithDebInfo"], + default="Release", help="Build type") + parser.add_argument("--verbose", "-v", action="store_true", + help="Verbose output") + parser.add_argument("--clean", "-c", action="store_true", + help="Clean build directory before building") + parser.add_argument("--no-vis", action="store_true", + help="Skip visualization") + parser.add_argument("--no-run", action="store_true", + help="Skip running simulation") + + args = parser.parse_args() + + # 创建自动化实例 + automator = CFDAutomation(build_type=args.build, verbose=args.verbose) + + # 清理选项 + if args.clean and automator.build_dir.exists(): + print("Cleaning build directory...") + shutil.rmtree(automator.build_dir) + + # 运行管道 + try: + automator.run_complete_pipeline() + except KeyboardInterrupt: + print("\n\n⚠ Process interrupted by user") + sys.exit(130) + except Exception as e: + print(f"\n✗ Automation failed: {e}") + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01g/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/cfd/01g/CMakeLists.txt new file mode 100644 index 00000000..25ed1aac --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01g/CMakeLists.txt @@ -0,0 +1,20 @@ +# CMakeLists.txt +cmake_minimum_required(VERSION 4.2.1) + +project(OneFLOW_CFD LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +set(SOURCES + cfd_solver.f90 +) + +add_executable(oneflow_cfd ${SOURCES}) + +target_include_directories(oneflow_cfd PRIVATE ${CMAKE_Fortran_MODULE_DIRECTORY}) diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01g/README.md b/example/1d-linear-convection/weno3/fortran/cfd/01g/README.md new file mode 100644 index 00000000..95888ac2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01g/README.md @@ -0,0 +1,26 @@ +# OneFLOW-CFD: 1D Convection Solver + +A Fortran implementation of 1D linear convection equation solver with ENO/WENO reconstruction schemes comparison. + +## Features + +- **Numerical Schemes**: ENO3 and WENO3 reconstruction +- **Time Integration**: Runge-Kutta methods (RK1, RK2) +- **Flux Schemes**: Rusanov and Engquist-Osher fluxes +- **Boundary Conditions**: Periodic boundary conditions +- **Visualization**: Python-based plotting and analysis +- **Automation**: Complete build-run-plot pipeline + +## Quick Start + +### Prerequisites +- Fortran compiler (Intel ifx/ifort, GNU gfortran, or LLVM flang) +- CMake 3.10+ +- Python 3.8+ with NumPy and Matplotlib + +### Installation + +1. Clone or download the project: + ```bash + git clone + cd OneFLOW-CFD-Fortran \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01g/cfd_solver.f90 b/example/1d-linear-convection/weno3/fortran/cfd/01g/cfd_solver.f90 new file mode 100644 index 00000000..909e5ace --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01g/cfd_solver.f90 @@ -0,0 +1,937 @@ +! OneFLOW-CFD Solver for 1D Convection Equation +! ENO/WENO Reconstruction Comparison - Single File Implementation + +module cfd_solver + use, intrinsic :: iso_fortran_env, only: dp => real64 + implicit none + + private + + ! =================================================================== + ! Forward Type Declarations + ! =================================================================== + type, public :: CfdConfigType + character(len=10) :: recon_scheme = "eno" + integer :: flux_type = 0 + integer :: rk_order = 1 + integer :: spatial_order = 3 + real(dp) :: wave_speed = 1.0_dp + real(dp) :: final_time = 0.625_dp + real(dp) :: dt = 0.025_dp ! 减小时间步长 + real(dp) :: cfl = 0.5_dp ! CFL数 + end type CfdConfigType + + type, public :: MeshType + real(dp) :: xmin = 0.0_dp + real(dp) :: xmax = 2.0_dp + integer :: ncells = 40 + integer :: nnodes = 0 + integer :: nx = 0 + real(dp) :: L = 0.0_dp + real(dp) :: dx = 0.0_dp + real(dp), allocatable :: x(:), xcc(:) + contains + procedure :: init => mesh_init + end type MeshType + + type, public :: ComputationalDomainType + type(MeshType) :: mesh + type(CfdConfigType) :: config + integer :: nghosts = 0 + integer :: ist = 0 + integer :: ied = 0 + integer :: ntcells = 0 + contains + procedure :: init => domain_init + end type ComputationalDomainType + + type, public :: SolutionType + type(ComputationalDomainType) :: domain + real(dp), allocatable :: q_face_left(:), q_face_right(:) + real(dp), allocatable :: flux(:), res(:) + real(dp), allocatable :: u(:), un(:) + contains + procedure :: init => solution_init + end type SolutionType + + ! Main CFD solver class + type, public :: CfdType + type(CfdConfigType) :: config + type(ComputationalDomainType) :: domain + type(SolutionType) :: solution + class(*), allocatable :: reconstructor + contains + procedure :: init => cfd_init + end type CfdType + + ! Abstract reconstructor base class + type, abstract, public :: ReconstructorType + contains + procedure(reconstruct_interface), deferred, pass :: reconstruct + end type ReconstructorType + + ! Define abstract interface + abstract interface + subroutine reconstruct_interface(this, q, cfd) + import :: ReconstructorType, CfdType, dp + class(ReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + end subroutine reconstruct_interface + end interface + + ! ENO reconstructor + type, extends(ReconstructorType), public :: EnoReconstructorType + integer :: spatial_order + integer :: ntcells + integer, allocatable :: lmc(:) + real(dp), allocatable :: coef(:,:) + real(dp), allocatable :: dd(:,:) + contains + procedure :: reconstruct => eno_reconstruct + procedure :: init => eno_init + end type EnoReconstructorType + + ! WENO reconstructor + type, extends(ReconstructorType), public :: WenoReconstructorType + contains + procedure :: reconstruct => weno_reconstruct + end type WenoReconstructorType + + ! =================================================================== + ! Public Procedures + ! =================================================================== + public :: run_simulation, init_field, analytical_solution, performEnoWenoAnalysis + + ! =================================================================== + ! Module Variables + ! =================================================================== + real(dp), parameter :: eps_weno = 1.0e-6_dp + +contains + + ! =================================================================== + ! Initialization Methods + ! =================================================================== + + ! Mesh initialization + subroutine mesh_init(this) + class(MeshType), intent(inout) :: this + integer :: i + + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, dp) + + allocate(this%x(this%nnodes), this%xcc(this%ncells)) + + ! Node coordinates + do i = 1, this%nnodes + this%x(i) = this%xmin + (i-1) * this%dx + end do + + ! Cell center coordinates + do i = 1, this%ncells + this%xcc(i) = 0.5_dp * (this%x(i) + this%x(i+1)) + end do + end subroutine mesh_init + + ! Domain initialization + subroutine domain_init(this, mesh, config) + class(ComputationalDomainType), intent(inout) :: this + type(MeshType), intent(in) :: mesh + type(CfdConfigType), intent(in) :: config + + this%mesh = mesh + this%config = config + + ! Calculate ghost cells + if (trim(config%recon_scheme) == "eno") then + this%nghosts = config%spatial_order + else if (trim(config%recon_scheme) == "weno") then + this%nghosts = config%spatial_order / 2 + 1 + else + error stop "Unknown reconstruction scheme" + end if + + this%ist = this%nghosts + 1 + this%ied = this%ist + mesh%ncells - 1 + this%ntcells = mesh%ncells + 2 * this%nghosts + + print *, "Domain initialized:" + print *, " mesh.ncells = ", mesh%ncells + print *, " spatial_order = ", config%spatial_order + print *, " nghosts = ", this%nghosts + print *, " ist = ", this%ist, ", ied = ", this%ied + print *, " dx = ", mesh%dx + end subroutine domain_init + + ! Solution initialization + subroutine solution_init(this, domain) + class(SolutionType), intent(inout) :: this + type(ComputationalDomainType), intent(in) :: domain + + this%domain = domain + + allocate(this%q_face_left(domain%mesh%nnodes)) + allocate(this%q_face_right(domain%mesh%nnodes)) + allocate(this%flux(domain%mesh%nnodes)) + allocate(this%res(domain%mesh%ncells)) + allocate(this%u(domain%ntcells)) + allocate(this%un(domain%ntcells)) + + this%q_face_left = 0.0_dp + this%q_face_right = 0.0_dp + this%flux = 0.0_dp + this%res = 0.0_dp + this%u = 0.0_dp + this%un = 0.0_dp + end subroutine solution_init + + ! ENO reconstructor initialization + subroutine eno_init(this, spatial_order, ntcells) + class(EnoReconstructorType), intent(inout) :: this + integer, intent(in) :: spatial_order + integer, intent(in) :: ntcells + + this%spatial_order = spatial_order + this%ntcells = ntcells + + allocate(this%lmc(ntcells)) + allocate(this%coef(spatial_order+1, spatial_order)) + allocate(this%dd(spatial_order, ntcells)) + + this%lmc = 0 + this%coef = 0.0_dp + this%dd = 0.0_dp + + ! Initialize coefficients + call init_coef(spatial_order, this%coef) + end subroutine eno_init + + ! CFD solver initialization + subroutine cfd_init(this, config, domain) + class(CfdType), intent(inout) :: this + type(CfdConfigType), intent(in) :: config + type(ComputationalDomainType), intent(in) :: domain + + this%config = config + this%domain = domain + call this%solution%init(domain) + + ! Create reconstructor based on scheme + if (trim(config%recon_scheme) == "eno") then + allocate(EnoReconstructorType :: this%reconstructor) + select type(rec => this%reconstructor) + type is (EnoReconstructorType) + call rec%init(config%spatial_order, domain%ntcells) + end select + else if (trim(config%recon_scheme) == "weno") then + allocate(WenoReconstructorType :: this%reconstructor) + else + error stop "Unknown reconstruction scheme" + end if + + ! Adjust time step based on CFL condition + call calculate_dt(this) + end subroutine cfd_init + + ! Calculate time step based on CFL condition + subroutine calculate_dt(cfd) + type(CfdType), intent(inout) :: cfd + + real(dp) :: dt_cfl + + ! CFL condition: dt <= CFL * dx / |wave_speed| + dt_cfl = cfd%config%cfl * cfd%domain%mesh%dx / abs(cfd%config%wave_speed) + + if (cfd%config%dt > dt_cfl) then + print *, "Adjusting time step for stability:" + print *, " Original dt = ", cfd%config%dt + print *, " CFL dt = ", dt_cfl + cfd%config%dt = dt_cfl + print *, " Using dt = ", cfd%config%dt + end if + + end subroutine calculate_dt + + ! =================================================================== + ! Initial Conditions and Analytical Solution + ! =================================================================== + + ! Initial condition: step function + function initial_condition(x) result(u0) + real(dp), intent(in) :: x + real(dp) :: u0 + + if (0.5_dp <= x .and. x <= 1.0_dp) then + u0 = 2.0_dp + else + u0 = 1.0_dp + end if + end function initial_condition + + ! Analytical solution with periodic BC + function analytical_solution(x, t, a, L) result(u) + real(dp), intent(in) :: x, t, a, L + real(dp) :: u, x_shifted + + x_shifted = mod(x - a * t + L, L) + u = initial_condition(x_shifted) + end function analytical_solution + + ! Initialize field with step function + subroutine init_field(cfd) + type(CfdType), intent(inout) :: cfd + integer :: i, j + + do i = 1, cfd%domain%mesh%ncells + if (0.5_dp <= cfd%domain%mesh%xcc(i) .and. cfd%domain%mesh%xcc(i) <= 1.0_dp) then + cfd%solution%u(cfd%domain%ist + i - 1) = 2.0_dp + else + cfd%solution%u(cfd%domain%ist + i - 1) = 1.0_dp + end if + end do + + call boundary(cfd%solution%u, cfd) + call update_oldfield(cfd%solution%un, cfd%solution%u, cfd%domain%ntcells) + end subroutine init_field + + ! =================================================================== + ! Boundary Conditions + ! =================================================================== + + ! Periodic boundary conditions - CORRECTED VERSION + subroutine periodic_boundary(u, cfd) + real(dp), intent(inout) :: u(:) + type(CfdType), intent(in) :: cfd + integer :: i + + ! Copy interior cells to ghost cells + ! Left ghost cells = right interior cells + do i = 1, cfd%domain%nghosts + u(cfd%domain%ist - i) = u(cfd%domain%ied - cfd%domain%nghosts + i - 1) + end do + + ! Right ghost cells = left interior cells + do i = 1, cfd%domain%nghosts + u(cfd%domain%ied + i) = u(cfd%domain%ist + i - 1) + end do + end subroutine periodic_boundary + + ! Boundary condition wrapper + subroutine boundary(u, cfd) + real(dp), intent(inout) :: u(:) + type(CfdType), intent(in) :: cfd + + call periodic_boundary(u, cfd) + end subroutine boundary + + ! =================================================================== + ! Reconstruction Methods + ! =================================================================== + + ! Initialize ENO/WENO coefficients + subroutine init_coef(spatial_order, coef) + integer, intent(in) :: spatial_order + real(dp), intent(out) :: coef(:,:) + + coef = 0.0_dp + + select case(spatial_order) + case(1) + coef(1,1) = 1.0_dp + coef(2,1) = 1.0_dp + + case(2) + coef(1,1:2) = [ 3.0_dp/2.0_dp, -1.0_dp/2.0_dp ] + coef(2,1:2) = [ 1.0_dp/2.0_dp, 1.0_dp/2.0_dp ] + coef(3,1:2) = [ -1.0_dp/2.0_dp, 3.0_dp/2.0_dp ] + + case(3) + coef(1,1:3) = [ 11.0_dp/6.0_dp, -7.0_dp/6.0_dp, 1.0_dp/3.0_dp ] + coef(2,1:3) = [ 1.0_dp/3.0_dp, 5.0_dp/6.0_dp, -1.0_dp/6.0_dp ] + coef(3,1:3) = [ -1.0_dp/6.0_dp, 5.0_dp/6.0_dp, 1.0_dp/3.0_dp ] + coef(4,1:3) = [ 1.0_dp/3.0_dp, -7.0_dp/6.0_dp, 11.0_dp/6.0_dp ] + + case default + error stop "Unsupported spatial order" + end select + end subroutine init_coef + + subroutine first_order_reconstruct(this, q, cfd) + class(EnoReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + integer :: i, j + integer :: nghosts, ist, ied + + nghosts = cfd%domain%nghosts + ist = cfd%domain%ist + ied = cfd%domain%ied + + ! For now, use simple 2nd order reconstruction + do i = ist, ied + j = i - ist + 1 ! 1-based index for interfaces + + cfd%solution%q_face_left(j) = q(i-1) + cfd%solution%q_face_right(j) = q(i) + end do + end subroutine first_order_reconstruct + + subroutine eno_reconstruct(this, q, cfd) + class(EnoReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + type(ComputationalDomainType), pointer :: domain + + integer :: i, j, m, k1, k2, r1, r2 + integer :: nghosts, ist, ied + + nghosts = cfd%domain%nghosts + ist = cfd%domain%ist + ied = cfd%domain%ied + + ! 1. 差商计算 (dd[1,:] = q) + this%dd(1, :) = q(:) + do m = 2, this%spatial_order + do j = 1, cfd%domain%ntcells - m + 1 + this%dd(m, j) = this%dd(m-1, j+1) - this%dd(m-1, j) + end do + end do + + ! 2. 选择 smoothest stencil + do i = ist - 1, ied ! Python: range(ist-1, ied+1) → ied+1-1 = ied + this%lmc(i) = i + do m = 2, this%spatial_order + if ( abs(this%dd(m, this%lmc(i) - 1) ) < abs(this%dd(m, this%lmc(i)))) then + this%lmc(i) = this%lmc(i) - 1 + end if + end do + end do + + associate ( & + q_face_left => cfd%solution%q_face_left, & + q_face_right => cfd%solution%q_face_right & + ) + ! 这里可以直接使用 q_face_left 和 q_face_right + ! 3. 重构界面值 + do i = ist, ied + j = i - ist + 1 ! 1-based index for interfaces + k1 = this%lmc(i - 1) + k2 = this%lmc(i) + r1 = (i - 1) - k1 + 1 + r2 = i - k2 + 1 + q_face_left(j) = 0.0 + q_face_right(j) = 0.0 + do m = 1, this%spatial_order + q_face_left(j) = q_face_left(j) + q(k1 + m - 1) * this%coef(r1 + 1, m) + q_face_right(j) = q_face_right(j) + q(k2 + m - 1) * this%coef(r2, m) + end do + end do + end associate + + end subroutine eno_reconstruct + + ! WENO-3 nonlinear weights for left-biased stencil + function wc3L(v1, v2, v3) result(f) + real(dp), intent(in) :: v1, v2, v3 + real(dp) :: f, s0, s1, d0, d1, c0, c1, w0, w1, q0, q1 + + ! Smoothness indicators + s0 = (v3 - v2)**2 + s1 = (v2 - v1)**2 + + ! Nonlinear weights + d0 = 2.0_dp/3.0_dp + d1 = 1.0_dp/3.0_dp + + c0 = d0 / ((eps_weno + s0)**2) + c1 = d1 / ((eps_weno + s1)**2) + + w0 = c0 / (c0 + c1) + w1 = c1 / (c0 + c1) + + ! Candidate stencils + q0 = 0.5_dp * v2 + 0.5_dp * v3 + q1 = -0.5_dp * v1 + 1.5_dp * v2 + + ! Reconstructed value + f = w0 * q0 + w1 * q1 + end function wc3L + + ! WENO-3 nonlinear weights for right-biased stencil + function wc3R(v1, v2, v3) result(f) + real(dp), intent(in) :: v1, v2, v3 + real(dp) :: f, s0, s1, d0, d1, c0, c1, w0, w1, q0, q1 + + ! Smoothness indicators + s0 = (v2 - v1)**2 + s1 = (v3 - v2)**2 + + ! Nonlinear weights + d0 = 2.0_dp/3.0_dp + d1 = 1.0_dp/3.0_dp + + c0 = d0 / ((eps_weno + s0)**2) + c1 = d1 / ((eps_weno + s1)**2) + + w0 = c0 / (c0 + c1) + w1 = c1 / (c0 + c1) + + ! Candidate stencils + q0 = 0.5_dp * v1 + 0.5_dp * v2 + q1 = 1.5_dp * v2 - 0.5_dp * v3 + + ! Reconstructed value + f = w0 * q0 + w1 * q1 + end function wc3R + + ! WENO-3 reconstruction for left interface + subroutine weno3L_periodic(cfd, u, qL) + type(CfdType), intent(in) :: cfd + real(dp), intent(in) :: u(:) + real(dp), intent(out) :: qL(:) + + integer :: i, j, nghosts, ist, ied + + nghosts = cfd%domain%nghosts + ist = cfd%domain%ist + ied = cfd%domain%ied + + do i = ist-1, ied-1 + j = i - (ist - 1) + 1 + qL(j) = wc3L(u(i-1), u(i), u(i+1)) + end do + + end subroutine weno3L_periodic + + ! WENO-3 reconstruction for right interface + subroutine weno3R_periodic(cfd, u, qR) + type(CfdType), intent(in) :: cfd + real(dp), intent(in) :: u(:) + real(dp), intent(out) :: qR(:) + + integer :: i, j, nghosts, ist, ied + + nghosts = cfd%domain%nghosts + ist = cfd%domain%ist + ied = cfd%domain%ied + + do i = ist, ied + j = i - ist + 1 + qR(j) = wc3R(u(i-1), u(i), u(i+1)) + end do + + end subroutine weno3R_periodic + + ! WENO reconstruction + subroutine weno_reconstruct(this, q, cfd) + class(WenoReconstructorType), intent(inout) :: this + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + call weno3L_periodic(cfd, q, cfd%solution%q_face_left) + call weno3R_periodic(cfd, q, cfd%solution%q_face_right) + end subroutine weno_reconstruct + + ! General reconstruction wrapper + subroutine reconstruction(q, cfd) + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + select type(rec => cfd%reconstructor) + type is (EnoReconstructorType) + call rec%reconstruct(q, cfd) + type is (WenoReconstructorType) + call rec%reconstruct(q, cfd) + class default + error stop "Unknown reconstructor type" + end select + end subroutine reconstruction + + ! =================================================================== + ! Flux Functions + ! =================================================================== + + ! Rusanov flux + subroutine rusanov_flux(q_face_left, q_face_right, flux, cfd) + real(dp), intent(in) :: q_face_left(:), q_face_right(:) + real(dp), intent(out) :: flux(:) + type(CfdType), intent(in) :: cfd + + integer :: i + real(dp) :: u_L, u_R, F_L, F_R, c_L, c_R, Smax + + c_L = cfd%config%wave_speed + c_R = cfd%config%wave_speed + + do i = 1, cfd%domain%mesh%nnodes + u_L = q_face_left(i) + u_R = q_face_right(i) + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux(i) = 0.5_dp * (F_L + F_R) - 0.5_dp * Smax * (u_R - u_L) + end do + end subroutine rusanov_flux + + ! Engquist-Osher flux + subroutine engquist_osher_flux(q_face_left, q_face_right, flux, cfd) + real(dp), intent(in) :: q_face_left(:), q_face_right(:) + real(dp), intent(out) :: flux(:) + type(CfdType), intent(in) :: cfd + + integer :: i + real(dp) :: c, cp, cm, u_L, u_R + + c = cfd%config%wave_speed + + do i = 1, cfd%domain%mesh%nnodes + cp = 0.5_dp * (c + abs(c)) + cm = 0.5_dp * (c - abs(c)) + u_L = q_face_left(i) + u_R = q_face_right(i) + flux(i) = cp * u_L + cm * u_R + end do + end subroutine engquist_osher_flux + + ! Inviscid flux selection + subroutine inviscid_flux(q_face_left, q_face_right, flux, cfd) + real(dp), intent(in) :: q_face_left(:), q_face_right(:) + real(dp), intent(out) :: flux(:) + type(CfdType), intent(in) :: cfd + + if (cfd%config%flux_type == 0) then + call rusanov_flux(q_face_left, q_face_right, flux, cfd) + else + call engquist_osher_flux(q_face_left, q_face_right, flux, cfd) + end if + end subroutine inviscid_flux + + ! =================================================================== + ! Residual Computation + ! =================================================================== + + ! Compute residual (flux divergence) - CORRECTED VERSION + subroutine residual(q, cfd) + real(dp), intent(in) :: q(:) + type(CfdType), intent(inout) :: cfd + + integer :: i + + ! Apply boundary conditions first + call boundary(cfd%solution%u, cfd) + + ! Reconstruction + call reconstruction(q, cfd) + + ! Compute fluxes + call inviscid_flux(cfd%solution%q_face_left, cfd%solution%q_face_right, & + cfd%solution%flux, cfd) + + ! Compute residual - corrected indexing + do i = 1, cfd%domain%mesh%ncells + cfd%solution%res(i) = -(cfd%solution%flux(i+1) - cfd%solution%flux(i)) / & + cfd%domain%mesh%dx + end do + end subroutine residual + + ! =================================================================== + ! Time Integration + ! =================================================================== + + ! Update old field + subroutine update_oldfield(qn, q, n) + real(dp), intent(out) :: qn(:) + real(dp), intent(in) :: q(:) + integer, intent(in) :: n + + qn(1:n) = q(1:n) + end subroutine update_oldfield + + ! 1st-order Runge-Kutta (Euler) - SIMPLIFIED + subroutine runge_kutta_1(cfd) + type(CfdType), intent(inout) :: cfd + + integer :: i, j + real(dp) :: dt + + dt = cfd%config%dt + + ! Apply boundary conditions + call boundary(cfd%solution%u, cfd) + + ! Compute residual + call residual(cfd%solution%u, cfd) + + ! Update solution + do i = cfd%domain%ist, cfd%domain%ied - 1 + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = cfd%solution%u(i) + dt * cfd%solution%res(j) + end do + + ! Apply boundary conditions again + call boundary(cfd%solution%u, cfd) + + ! Save old solution + call update_oldfield(cfd%solution%un, cfd%solution%u, cfd%domain%ntcells) + end subroutine runge_kutta_1 + + ! 2nd-order Runge-Kutta (Heun) + subroutine runge_kutta_2(cfd) + type(CfdType), intent(inout) :: cfd + + integer :: i, j + real(dp) :: dt + + dt = cfd%config%dt + + ! Stage 1 + call boundary(cfd%solution%u, cfd) + call residual(cfd%solution%u, cfd) + + do i = cfd%domain%ist, cfd%domain%ied - 1 + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = cfd%solution%u(i) + dt * cfd%solution%res(j) + end do + call boundary(cfd%solution%u, cfd) + + ! Stage 2 + call residual(cfd%solution%u, cfd) + do i = cfd%domain%ist, cfd%domain%ied - 1 + j = i - cfd%domain%ist + 1 + cfd%solution%u(i) = 0.5_dp * cfd%solution%un(i) + & + 0.5_dp * cfd%solution%u(i) + & + 0.5_dp * dt * cfd%solution%res(j) + end do + call boundary(cfd%solution%u, cfd) + + call update_oldfield(cfd%solution%un, cfd%solution%u, cfd%domain%ntcells) + end subroutine runge_kutta_2 + + ! Runge-Kutta selection + subroutine runge_kutta(cfd) + type(CfdType), intent(inout) :: cfd + + select case(cfd%config%rk_order) + case(1) + call runge_kutta_1(cfd) + case(2) + call runge_kutta_2(cfd) + case default + call runge_kutta_1(cfd) + end select + end subroutine runge_kutta + + ! =================================================================== + ! Simulation Driver + ! =================================================================== + + ! Run simulation to final time + function run_simulation(cfd, final_time) result(u_result) + type(CfdType), intent(inout) :: cfd + real(dp), intent(in) :: final_time + real(dp), allocatable :: u_result(:) + + real(dp) :: t, dt, dt_old + integer :: step, max_steps + + allocate(u_result(cfd%domain%mesh%ncells)) + + t = 0.0_dp + dt_old = cfd%config%dt + dt = dt_old + max_steps = 10000 ! Safety limit + + print *, "Starting time integration..." + print *, " Final time: ", final_time + print *, " Time step: ", dt + print *, " CFL number: ", cfd%config%cfl + + step = 0 + do while (t < final_time - 1.0e-12_dp .and. step < max_steps) + step = step + 1 + + if (t + dt > final_time) then + dt = final_time - t + end if + + cfd%config%dt = dt + call runge_kutta(cfd) + t = t + dt + + ! Progress report + if (mod(step, 100) == 0) then + print *, " Step ", step, ", Time = ", t + end if + end do + + if (step >= max_steps) then + print *, "Warning: Reached maximum number of steps (", max_steps, ")" + end if + + cfd%config%dt = dt_old + + print *, "Time integration completed:" + print *, " Total steps: ", step + print *, " Final time: ", t + + ! Extract physical solution (without ghost cells) + u_result = cfd%solution%u(cfd%domain%ist:cfd%domain%ied) + end function run_simulation + + ! =================================================================== + ! Main Analysis Function + ! =================================================================== + + ! Perform ENO-WENO comparative analysis + subroutine performEnoWenoAnalysis() + type(CfdConfigType) :: config_eno3, config_weno3 + type(MeshType) :: mesh + type(ComputationalDomainType) :: domain_eno3, domain_weno3 + type(CfdType) :: cfd_eno3, cfd_weno3 + real(dp), allocatable :: u_eno(:), u_weno(:), u_analytical(:) + real(dp), allocatable :: xcc(:) + integer :: i, ncells, iunit + + ! Initialize mesh + call mesh%init() + ncells = mesh%ncells + allocate(xcc(ncells)) + xcc = mesh%xcc + + print *, "==========================================" + print *, "Mesh parameters:" + print *, " ncells = ", ncells + print *, " dx = ", mesh%dx + print *, " L = ", mesh%L + print *, "==========================================" + + ! Configure ENO3 - using simple 2nd order for stability + config_eno3%recon_scheme = "eno" + config_eno3%spatial_order = 3 + config_eno3%flux_type = 0 + config_eno3%rk_order = 1 + config_eno3%wave_speed = 1.0_dp + config_eno3%final_time = 0.625_dp + config_eno3%cfl = 1.0_dp + config_eno3%dt = 0.0025_dp + + ! Configure WENO3 + config_weno3%recon_scheme = "weno" + config_weno3%spatial_order = 3 + config_weno3%flux_type = 0 + config_weno3%rk_order = 1 + config_weno3%wave_speed = 1.0_dp + config_weno3%final_time = 0.625_dp + config_weno3%cfl = 1.0_dp + config_weno3%dt = 0.0025_dp + + ! Create domains + call domain_eno3%init(mesh, config_eno3) + call domain_weno3%init(mesh, config_weno3) + + ! Create CFD solvers + call cfd_eno3%init(config_eno3, domain_eno3) + call cfd_weno3%init(config_weno3, domain_weno3) + + ! Allocate arrays + allocate(u_eno(ncells), u_weno(ncells), u_analytical(ncells)) + + ! Run ENO simulation + print *, "==========================================" + print *, "Running ENO simulation..." + print *, " Scheme: ENO", config_eno3%spatial_order + print *, " Time step: ", config_eno3%dt + print *, "==========================================" + + call init_field(cfd_eno3) + u_eno = run_simulation(cfd_eno3, config_eno3%final_time) + + ! Run WENO simulation + print *, "==========================================" + print *, "Running WENO simulation..." + print *, " Scheme: WENO", config_weno3%spatial_order + print *, " Time step: ", config_weno3%dt + print *, "==========================================" + + call init_field(cfd_weno3) + u_weno = run_simulation(cfd_weno3, config_weno3%final_time) + + ! Compute analytical solution + print *, "Computing analytical solution..." + do i = 1, ncells + u_analytical(i) = analytical_solution(xcc(i), config_weno3%final_time, & + config_weno3%wave_speed, mesh%L) + end do + + ! Write results to files + print *, "Writing results to files..." + + ! Write ENO results + open(newunit=iunit, file='eno_results.txt', status='replace') + write(iunit, '(A)') '# x, u (ENO)' + do i = 1, ncells + write(iunit, '(2F12.6)') xcc(i), u_eno(i) + end do + close(iunit) + + ! Write WENO results + open(newunit=iunit, file='weno_results.txt', status='replace') + write(iunit, '(A)') '# x, u (WENO)' + do i = 1, ncells + write(iunit, '(2F12.6)') xcc(i), u_weno(i) + end do + close(iunit) + + ! Write analytical results + open(newunit=iunit, file='analytical_results.txt', status='replace') + write(iunit, '(A)') '# x, u (Analytical)' + do i = 1, ncells + write(iunit, '(2F12.6)') xcc(i), u_analytical(i) + end do + close(iunit) + + ! Print some statistics + print *, "==========================================" + print *, "Simulation statistics:" + print *, " ENO min/max: ", minval(u_eno), maxval(u_eno) + print *, " WENO min/max: ", minval(u_weno), maxval(u_weno) + print *, " Analytical min/max: ", minval(u_analytical), maxval(u_analytical) + print *, "==========================================" + + print *, "Simulation completed successfully!" + print *, "Results written to:" + print *, " eno_results.txt" + print *, " weno_results.txt" + print *, " analytical_results.txt" + print *, "" + print *, "To generate the comparison plot, run:" + print *, " python postprocess.py" + print *, "==========================================" + + deallocate(u_eno, u_weno, u_analytical, xcc) + end subroutine performEnoWenoAnalysis + +end module cfd_solver + +! =================================================================== +! Main Program +! =================================================================== +program main + use cfd_solver + implicit none + + print *, "==========================================" + print *, "OneFLOW-CFD Solver for 1D Convection" + print *, "ENO vs WENO Comparison" + print *, "==========================================" + + call performEnoWenoAnalysis() + + print *, "Program finished successfully!" + +end program main \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01g/postprocess.py b/example/1d-linear-convection/weno3/fortran/cfd/01g/postprocess.py new file mode 100644 index 00000000..129cb9ad --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01g/postprocess.py @@ -0,0 +1,52 @@ +import numpy as np +import matplotlib.pyplot as plt + +def read_results(filename): + """Read results from Fortran output file""" + try: + data = np.loadtxt(filename, comments='#') + return data[:, 0], data[:, 1] + except Exception as e: + print(f"Error reading {filename}: {e}") + return np.array([]), np.array([]) + +def main(): + print("Reading Fortran output files...") + + # Read all data files + x_eno, u_eno = read_results('eno_results.txt') + x_weno, u_weno = read_results('weno_results.txt') + x_analytical, u_analytical = read_results('analytical_results.txt') + + # Check if we have data + if len(x_eno) == 0 or len(x_weno) == 0 or len(x_analytical) == 0: + print("Error: Could not read all data files.") + print("Make sure to run the Fortran program first.") + return + + # Create plot + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # Plot results + plt.plot(x_eno, u_eno, 'bo-', linewidth=1, markersize=5, + markerfacecolor='none', label='ENO3') + plt.plot(x_weno, u_weno, 'gs-', linewidth=1, markersize=5, + markerfacecolor='none', label='WENO3') + plt.plot(x_analytical, u_analytical, 'r-', linewidth=2, label='Analytical') + + # Customize plot + plt.title('1D Convection: ENO3 vs WENO3 (t=0.625)') + plt.xlabel('x') + plt.ylabel('u') + plt.legend() + plt.grid(True, alpha=0.3) + + # Save and show + plt.tight_layout() + plt.savefig('comparison.png', dpi=150) + plt.show() + + print("Plot saved as comparison.png") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/cfd/01g/run_all.py b/example/1d-linear-convection/weno3/fortran/cfd/01g/run_all.py new file mode 100644 index 00000000..44bf7c89 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/cfd/01g/run_all.py @@ -0,0 +1,482 @@ +#!/usr/bin/env python3 +""" +OneFLOW-CFD Complete Automation System +Author: Your Name +Date: 2024 +Description: Automated build, run, and visualization pipeline +""" + +import os +import sys +import subprocess +import shutil +import time +from pathlib import Path +import argparse + +class CFDAutomation: + """自动化CFD求解器的完整流程""" + + def __init__(self, build_type="Release", verbose=False): + self.project_root = Path.cwd() + self.build_dir = self.project_root / "build" + self.bin_dir = None + self.executable = None + self.build_type = build_type + self.verbose = verbose + self.results_dir = self.project_root / "results" + # 初始化combined_env,避免属性不存在报错 + self.combined_env = os.environ.copy() + + def print_banner(self): + """打印漂亮的横幅""" + print("\n" + "="*70) + print(" OneFLOW-CFD Automation System") + print(" 1D Convection: ENO3 vs WENO3 Comparison") + print("="*70 + "\n") + + def print_step(self, step_num, description): + """打印步骤信息""" + print(f"[{step_num}/7] {description}") + print("-" * 50) + + def check_environment(self): + """检查运行环境""" + self.print_step(2, "Checking Environment") # 调整步骤号,匹配后续流程 + + # 检查Python + try: + import numpy, matplotlib + print("✓ NumPy and Matplotlib are available") + except ImportError as e: + print(f"✗ Missing Python packages: {e}") + print("Installing required packages...") + try: + subprocess.run([sys.executable, "-m", "pip", "install", "numpy", "matplotlib", "-q"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + shell=True) + print("✓ Packages installed") + except Exception as install_e: + print(f"✗ Package installation failed: {install_e}") + sys.exit(1) + + # 检查Fortran编译器 + compilers = ["ifx", "ifort", "gfortran", "flang"] + found = False + for compiler in compilers: + try: + result = subprocess.run([compiler, "--version"], + env=self.combined_env, + capture_output=True, + text=True, + encoding='utf-8', # 替换为utf-8,避免gbk解码失败 + errors='ignore', # 忽略无效字符,兜底报错 + shell=True) + if result.returncode == 0: + print(f"✓ Found Fortran compiler: {compiler}") + found = True + break + except (FileNotFoundError, Exception): + continue + + if not found: + print("⚠ Warning: No Fortran compiler found in PATH") + print(" On Windows, please run from Intel oneAPI command prompt") + print(" On Linux/Mac, install: ifx, gfortran, or flang") + + # 检查CMake + try: + subprocess.run(["cmake", "--version"], + env=self.combined_env, + capture_output=True, + text=True, + encoding='utf-8', # 替换为utf-8 + errors='ignore', # 兜底无效字符 + shell=True) + print("✓ CMake is available") + except FileNotFoundError: + print("✗ CMake not found. Please install CMake 3.10+") + sys.exit(1) + + def setup_intel_environment(self): + """一次性提取setvars.bat配置后的所有环境变量(仅执行一次)""" + self.print_step(1, "Setting up Intel oneAPI Environment") # 明确步骤1 + + cmd_command = r'cmd.exe /C ""C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && set"' + + try: + result = subprocess.run( + cmd_command, + capture_output=True, + text=True, + encoding='gbk', # 保留gbk解析cmd输出 + errors='ignore', # 新增:忽略无法解码的字符,避免报错 + shell=True, + timeout=30 + ) + except Exception as e: + raise Exception(f"提取oneAPI环境失败:{str(e)}") + + if result.returncode != 0: + raise Exception(f"setvars.bat执行失败:{result.stderr}") + + # 解析环境变量(忽略非有效行) + env_dict = {} + for line in result.stdout.splitlines(): + line = line.strip() + if '=' in line and not line.startswith('='): + try: + key, value = line.split('=', 1) + env_dict[key.strip()] = value.strip() + except: + continue # 忽略分割失败的行,避免中断解析 + + oneapi_env = env_dict + + # 合并环境变量(系统原有环境 + oneAPI环境) + self.combined_env = os.environ.copy() + self.combined_env.update(oneapi_env) + + print("✓ Intel oneAPI environment loaded successfully") + return oneapi_env + + def configure_cmake(self): + """配置CMake""" + self.print_step(3, "Configuring with CMake") + + # 清理旧的构建目录 + if self.build_dir.exists(): + print("Cleaning previous build...") + shutil.rmtree(self.build_dir) + + # 创建构建目录 + self.build_dir.mkdir(exist_ok=True) + + # CMake命令 + cmake_cmd = ["cmake", "-S", str(self.project_root), "-B", str(self.build_dir)] + + # 平台特定选项 + if sys.platform == "win32": + cmake_cmd.extend(["-G", "Visual Studio 17 2022", "-A", "x64"]) + cmake_cmd.extend(["-T", "fortran=ifx"]) + else: + cmake_cmd.extend(["-DCMAKE_BUILD_TYPE=" + self.build_type]) + + print(f"Running: {' '.join(cmake_cmd)}") + + try: + # 核心修改:指定编码为utf-8,添加errors='ignore' + result = subprocess.run(cmake_cmd, check=True, + capture_output=not self.verbose, + env=self.combined_env, + text=True, + encoding='utf-8', + errors='ignore', + shell=True) + if self.verbose and result.stdout: + print(result.stdout) + print("✓ CMake configuration successful") + except subprocess.CalledProcessError as e: + print(f"✗ CMake configuration failed:") + if e.stderr: + print(e.stderr) + sys.exit(1) + + def build_project(self): + """构建项目""" + self.print_step(4, "Building Project") + + build_cmd = ["cmake", "--build", str(self.build_dir), "--config", self.build_type] + + if self.verbose: + build_cmd.append("--verbose") + + print(f"Running: {' '.join(build_cmd)}") + + try: + # 核心修改:指定编码为utf-8,添加errors='ignore',解决UnicodeDecodeError + result = subprocess.run(build_cmd, check=True, + capture_output=not self.verbose, + env=self.combined_env, + text=True, + encoding='utf-8', + errors='ignore', + shell=True) + if self.verbose and result.stdout: + print(result.stdout) + print("✓ Build successful") + + # 查找可执行文件 + self._find_executable() + + except subprocess.CalledProcessError as e: + print(f"✗ Build failed:") + if e.stderr: + print(e.stderr) + sys.exit(1) + + def _find_executable(self): + """查找生成的可执行文件""" + search_paths = [] + + if sys.platform == "win32": + search_paths = [ + self.build_dir / "bin" / self.build_type / "oneflow_cfd.exe", + self.build_dir / self.build_type / "oneflow_cfd.exe", + ] + else: + search_paths = [ + self.build_dir / "oneflow_cfd", + self.build_dir / "bin" / "oneflow_cfd", + ] + + for path in search_paths: + if path.exists(): + self.executable = path + self.bin_dir = path.parent + print(f"✓ Found executable: {self.executable}") + return + + print("⚠ Could not find executable automatically") + print(" Searched in:", [str(p) for p in search_paths]) + + def run_simulation(self): + """运行CFD模拟""" + self.print_step(5, "Running CFD Simulation") + + if not self.executable or not self.executable.exists(): + print("✗ Executable not found!") + return False + + # 确保postprocess.py在运行目录 + postprocess_src = self.project_root / "postprocess.py" + if postprocess_src.exists(): + shutil.copy2(postprocess_src, self.bin_dir / "postprocess.py") + + # 运行可执行文件 + print(f"Running: {self.executable.name}") + print("-" * 50) + + try: + original_dir = os.getcwd() + os.chdir(self.bin_dir) + + result = subprocess.run([str(self.executable)], + check=True, + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + env=self.combined_env, + shell=True) + + # 打印输出 + if result.stdout: + lines = result.stdout.strip().split('\n') + for line in lines: + if any(keyword in line.lower() for keyword in + ['error', 'warning', 'failed', 'success']): + if 'error' in line.lower() or 'failed' in line.lower(): + print(f"✗ {line}") + elif 'warning' in line.lower(): + print(f"⚠ {line}") + else: + print(f"✓ {line}") + else: + print(f" {line}") + + if result.stderr: + print("\nStderr output:") + print(result.stderr) + + print("-" * 50) + print("✓ Simulation completed") + + os.chdir(original_dir) + return True + + except subprocess.CalledProcessError as e: + print(f"✗ Simulation failed with exit code {e.returncode}") + if e.stdout: + print("Output:", e.stdout) + os.chdir(original_dir) + return False + + def generate_visualization(self): + """生成可视化结果""" + self.print_step(6, "Generating Visualization") + + original_dir = os.getcwd() + os.chdir(self.bin_dir) + + postprocess_script = self.bin_dir / "postprocess.py" + if not postprocess_script.exists(): + print("✗ postprocess.py not found in output directory") + os.chdir(original_dir) + return False + + print(f"Running: python {postprocess_script.name}") + + try: + result = subprocess.run([sys.executable, str(postprocess_script)], + check=True, + capture_output=True, + text=True, + encoding='utf-8', # 新增:指定utf-8编码 + errors='ignore', # 新增:忽略无效字符 + env=self.combined_env, + shell=True) + + if result.stdout and self.verbose: + print(result.stdout) + + print("✓ Visualization generated") + os.chdir(original_dir) + return True + + except subprocess.CalledProcessError as e: + print(f"✗ Visualization failed: {e}") + os.chdir(original_dir) + return False + + def organize_results(self): + """整理结果文件""" + self.print_step(7, "Organizing Results") + + # 创建结果目录 + self.results_dir.mkdir(exist_ok=True) + + # 复制结果文件 + result_patterns = ["*results.txt", "comparison.png", "*.dat"] + + for pattern in result_patterns: + for result_file in self.bin_dir.glob(pattern): + if result_file.is_file(): + dest = self.results_dir / result_file.name + # 避免同名文件覆盖提示,直接复制 + shutil.copy2(result_file, dest) + print(f"✓ Copied: {result_file.name}") + + # 创建结果摘要 + self._create_summary() + + print(f"\n✓ Results organized in: {self.results_dir}") + + def _create_summary(self): + """创建结果摘要""" + summary_file = self.results_dir / "simulation_summary.txt" + + with open(summary_file, 'w', encoding='utf-8') as f: # 指定utf-8写入,避免中文乱码 + f.write("OneFLOW-CFD Simulation Summary\n") + f.write("=" * 50 + "\n") + f.write(f"Date: {time.strftime('%Y-%m-%d %H:%M:%S')}\n") + f.write(f"Build Type: {self.build_type}\n") + f.write(f"Platform: {sys.platform}\n") + f.write("\nFiles Generated:\n") + + for file in sorted(self.results_dir.glob("*")): + if file.is_file(): + size_kb = file.stat().st_size / 1024 + f.write(f" - {file.name:20} {size_kb:6.1f} KB\n") + + def run_complete_pipeline(self): + """运行完整管道""" + self.print_banner() + + steps = [ + self.setup_intel_environment, + self.check_environment, + self.configure_cmake, + self.build_project, + self.run_simulation, + self.generate_visualization, + self.organize_results, + ] + + for i, step in enumerate(steps, 1): + try: + step() + except Exception as e: + print(f"\n✗ Step {i} failed: {e}") + print("\nDebug information:") + print(f" Project root: {self.project_root}") + print(f" Build dir: {self.build_dir}") + print(f" Executable: {self.executable}") + sys.exit(1) + + self._print_completion_message() + + def _print_completion_message(self): + """打印完成信息""" + print("\n" + "="*70) + print(" OneFLOW-CFD Automation Complete!") + print("="*70) + print("\nGenerated Files:") + print(" Results Directory: results/") + + for file in sorted(self.results_dir.glob("*")): + if file.is_file(): + print(f" - {file.name}") + + print("\nTo view the plot:") + plot_file = self.results_dir / "comparison.png" + if plot_file.exists(): + if sys.platform == "win32": + print(f" start results/comparison.png") + elif sys.platform == "darwin": + print(f" open results/comparison.png") + else: + print(f" xdg-open results/comparison.png") + + print("\n" + "="*70) + +def main(): + """主函数""" + parser = argparse.ArgumentParser( + description="OneFLOW-CFD Automation System", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + %(prog)s # Run complete pipeline + %(prog)s --verbose # Verbose output + %(prog)s --build Debug # Debug build + %(prog)s --clean # Clean before building + """ + ) + + parser.add_argument("--build", choices=["Release", "Debug", "RelWithDebInfo"], + default="Release", help="Build type") + parser.add_argument("--verbose", "-v", action="store_true", + help="Verbose output") + parser.add_argument("--clean", "-c", action="store_true", + help="Clean build directory before building") + parser.add_argument("--no-vis", action="store_true", + help="Skip visualization") + parser.add_argument("--no-run", action="store_true", + help="Skip running simulation") + + args = parser.parse_args() + + # 创建自动化实例 + automator = CFDAutomation(build_type=args.build, verbose=args.verbose) + + # 清理选项 + if args.clean and automator.build_dir.exists(): + print("Cleaning build directory...") + shutil.rmtree(automator.build_dir) + + # 运行管道 + try: + automator.run_complete_pipeline() + except KeyboardInterrupt: + print("\n\n⚠ Process interrupted by user") + sys.exit(130) + except Exception as e: + print(f"\n✗ Automation failed: {e}") + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/01/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/01/CMakeLists.txt new file mode 100644 index 00000000..2de8232f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/01/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.20) + +project ( testprj ) + +set ( PRJ_COMPILE_FEATURES ) +set ( PRJ_COMPILE_DEFINITIONS ) + +enable_language(Fortran) + +list ( APPEND PRJ_COMPILE_FEATURES cxx_std_20 ) + +add_executable( ${PROJECT_NAME} + main.f90 +) + +target_compile_features ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/01/main.f90 b/example/1d-linear-convection/weno3/fortran/grammar/01/main.f90 new file mode 100644 index 00000000..2089046d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/01/main.f90 @@ -0,0 +1,3 @@ +program main +write(*,*) "haha" +end program main \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/01a/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/01a/CMakeLists.txt new file mode 100644 index 00000000..2de8232f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/01a/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.20) + +project ( testprj ) + +set ( PRJ_COMPILE_FEATURES ) +set ( PRJ_COMPILE_DEFINITIONS ) + +enable_language(Fortran) + +list ( APPEND PRJ_COMPILE_FEATURES cxx_std_20 ) + +add_executable( ${PROJECT_NAME} + main.f90 +) + +target_compile_features ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/01a/main.f90 b/example/1d-linear-convection/weno3/fortran/grammar/01a/main.f90 new file mode 100644 index 00000000..72196696 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/01a/main.f90 @@ -0,0 +1,22 @@ +program main_prog +implicit none + + call sub1 + call sub2 + +end program main_prog + + +subroutine sub1() +implicit none + + print*,"haha1" + +end subroutine sub1 + +subroutine sub2() +implicit none + + print*,"haha2" + +end subroutine sub2 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/01b/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/01b/CMakeLists.txt new file mode 100644 index 00000000..3dad6fe2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/01b/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.20) + +project ( testprj ) + +set ( PRJ_COMPILE_FEATURES ) +set ( PRJ_COMPILE_DEFINITIONS ) + +enable_language(Fortran) + +list ( APPEND PRJ_COMPILE_FEATURES cxx_std_20 ) + +add_executable( ${PROJECT_NAME} + main.f90 + sub1.f90 + sub2.f90 +) + +target_compile_features ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/01b/main.f90 b/example/1d-linear-convection/weno3/fortran/grammar/01b/main.f90 new file mode 100644 index 00000000..77996d47 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/01b/main.f90 @@ -0,0 +1,7 @@ +program main_prog +implicit none + + call sub1 + call sub2 + +end program main_prog \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/01b/sub1.f90 b/example/1d-linear-convection/weno3/fortran/grammar/01b/sub1.f90 new file mode 100644 index 00000000..d5f0f704 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/01b/sub1.f90 @@ -0,0 +1,6 @@ +subroutine sub1() +implicit none + + print*,"haha1" + +end subroutine sub1 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/01b/sub2.f90 b/example/1d-linear-convection/weno3/fortran/grammar/01b/sub2.f90 new file mode 100644 index 00000000..a8fd6f8a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/01b/sub2.f90 @@ -0,0 +1,6 @@ +subroutine sub2() +implicit none + + print*,"haha2" + +end subroutine sub2 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/01c/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/01c/CMakeLists.txt new file mode 100644 index 00000000..2de8232f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/01c/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.20) + +project ( testprj ) + +set ( PRJ_COMPILE_FEATURES ) +set ( PRJ_COMPILE_DEFINITIONS ) + +enable_language(Fortran) + +list ( APPEND PRJ_COMPILE_FEATURES cxx_std_20 ) + +add_executable( ${PROJECT_NAME} + main.f90 +) + +target_compile_features ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/01c/main.f90 b/example/1d-linear-convection/weno3/fortran/grammar/01c/main.f90 new file mode 100644 index 00000000..5d628147 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/01c/main.f90 @@ -0,0 +1,33 @@ +module mymod1 +implicit none + + integer, parameter :: N = 1024 + +contains + subroutine show_N() + print*, "N = ", N + end subroutine show_N + +end module mymod1 + +module mymod2 +implicit none + + integer, parameter :: M = 256 + +contains + subroutine show_M() + print*, "M = ", M + end subroutine show_M + +end module mymod2 + +program main_prog +use mymod1 +use mymod2 +implicit none + + call show_N() + call show_M() + +end program main_prog \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/01d/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/01d/CMakeLists.txt new file mode 100644 index 00000000..f0796000 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/01d/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.20) + +project ( testprj ) + +set ( PRJ_COMPILE_FEATURES ) +set ( PRJ_COMPILE_DEFINITIONS ) + +enable_language(Fortran) + +list ( APPEND PRJ_COMPILE_FEATURES cxx_std_20 ) + +add_executable( ${PROJECT_NAME} + main.f90 + mymod1.f90 + mymod2.f90 +) + +target_compile_features ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/01d/main.f90 b/example/1d-linear-convection/weno3/fortran/grammar/01d/main.f90 new file mode 100644 index 00000000..5d9d514a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/01d/main.f90 @@ -0,0 +1,9 @@ +program main_prog +use mymod1 +use mymod2 +implicit none + + call show_N() + call show_M() + +end program main_prog \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/01d/mymod1.f90 b/example/1d-linear-convection/weno3/fortran/grammar/01d/mymod1.f90 new file mode 100644 index 00000000..49f99c96 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/01d/mymod1.f90 @@ -0,0 +1,11 @@ +module mymod1 +implicit none + + integer, parameter :: N = 1024 + +contains + subroutine show_N() + print*, "N = ", N + end subroutine show_N + +end module mymod1 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/01d/mymod2.f90 b/example/1d-linear-convection/weno3/fortran/grammar/01d/mymod2.f90 new file mode 100644 index 00000000..b265eeb9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/01d/mymod2.f90 @@ -0,0 +1,11 @@ +module mymod2 +implicit none + + integer, parameter :: M = 256 + +contains + subroutine show_M() + print*, "M = ", M + end subroutine show_M + +end module mymod2 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/01e/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/01e/CMakeLists.txt new file mode 100644 index 00000000..b96ef5bb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/01e/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.20) + +project ( testprj ) + +set ( PRJ_COMPILE_FEATURES ) +set ( PRJ_COMPILE_DEFINITIONS ) + +enable_language(Fortran) + +list ( APPEND PRJ_COMPILE_FEATURES cxx_std_20 ) + +add_executable( ${PROJECT_NAME} + main.f90 + src/mymod1.f90 + src/mymod2.f90 +) + +target_compile_features ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/01e/main.f90 b/example/1d-linear-convection/weno3/fortran/grammar/01e/main.f90 new file mode 100644 index 00000000..5d9d514a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/01e/main.f90 @@ -0,0 +1,9 @@ +program main_prog +use mymod1 +use mymod2 +implicit none + + call show_N() + call show_M() + +end program main_prog \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/01e/src/mymod1.f90 b/example/1d-linear-convection/weno3/fortran/grammar/01e/src/mymod1.f90 new file mode 100644 index 00000000..49f99c96 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/01e/src/mymod1.f90 @@ -0,0 +1,11 @@ +module mymod1 +implicit none + + integer, parameter :: N = 1024 + +contains + subroutine show_N() + print*, "N = ", N + end subroutine show_N + +end module mymod1 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/01e/src/mymod2.f90 b/example/1d-linear-convection/weno3/fortran/grammar/01e/src/mymod2.f90 new file mode 100644 index 00000000..b265eeb9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/01e/src/mymod2.f90 @@ -0,0 +1,11 @@ +module mymod2 +implicit none + + integer, parameter :: M = 256 + +contains + subroutine show_M() + print*, "M = ", M + end subroutine show_M + +end module mymod2 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/01f/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/01f/CMakeLists.txt new file mode 100644 index 00000000..b96ef5bb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/01f/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.20) + +project ( testprj ) + +set ( PRJ_COMPILE_FEATURES ) +set ( PRJ_COMPILE_DEFINITIONS ) + +enable_language(Fortran) + +list ( APPEND PRJ_COMPILE_FEATURES cxx_std_20 ) + +add_executable( ${PROJECT_NAME} + main.f90 + src/mymod1.f90 + src/mymod2.f90 +) + +target_compile_features ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/01f/main.f90 b/example/1d-linear-convection/weno3/fortran/grammar/01f/main.f90 new file mode 100644 index 00000000..5d9d514a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/01f/main.f90 @@ -0,0 +1,9 @@ +program main_prog +use mymod1 +use mymod2 +implicit none + + call show_N() + call show_M() + +end program main_prog \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/01f/src/mymod1.f90 b/example/1d-linear-convection/weno3/fortran/grammar/01f/src/mymod1.f90 new file mode 100644 index 00000000..49f99c96 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/01f/src/mymod1.f90 @@ -0,0 +1,11 @@ +module mymod1 +implicit none + + integer, parameter :: N = 1024 + +contains + subroutine show_N() + print*, "N = ", N + end subroutine show_N + +end module mymod1 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/01f/src/mymod2.f90 b/example/1d-linear-convection/weno3/fortran/grammar/01f/src/mymod2.f90 new file mode 100644 index 00000000..b265eeb9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/01f/src/mymod2.f90 @@ -0,0 +1,11 @@ +module mymod2 +implicit none + + integer, parameter :: M = 256 + +contains + subroutine show_M() + print*, "M = ", M + end subroutine show_M + +end module mymod2 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/02/CMakeLists.txt new file mode 100644 index 00000000..bec06c79 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.20) + +project ( testprj ) + +set ( PRJ_COMPILE_FEATURES ) +set ( PRJ_COMPILE_DEFINITIONS ) + +enable_language(Fortran) + +list ( APPEND PRJ_COMPILE_FEATURES cxx_std_20 ) + +add_executable( ${PROJECT_NAME} + main.f90 + src/sub1.f90 + src/sub2.f90 +) + +target_compile_features ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02/main.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02/main.f90 new file mode 100644 index 00000000..77996d47 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02/main.f90 @@ -0,0 +1,7 @@ +program main_prog +implicit none + + call sub1 + call sub2 + +end program main_prog \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02/src/sub1.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02/src/sub1.f90 new file mode 100644 index 00000000..d5f0f704 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02/src/sub1.f90 @@ -0,0 +1,6 @@ +subroutine sub1() +implicit none + + print*,"haha1" + +end subroutine sub1 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02/src/sub2.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02/src/sub2.f90 new file mode 100644 index 00000000..a8fd6f8a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02/src/sub2.f90 @@ -0,0 +1,6 @@ +subroutine sub2() +implicit none + + print*,"haha2" + +end subroutine sub2 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02a/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/02a/CMakeLists.txt new file mode 100644 index 00000000..45cabcfb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02a/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.20) + +project ( testprj ) + +set ( PRJ_COMPILE_FEATURES ) +set ( PRJ_COMPILE_DEFINITIONS ) + +enable_language(Fortran) + +list ( APPEND PRJ_COMPILE_FEATURES cxx_std_20 ) + +add_executable( ${PROJECT_NAME} + test/main.f90 + src/sub1.f90 + src/sub2.f90 +) + +target_compile_features ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02a/src/sub1.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02a/src/sub1.f90 new file mode 100644 index 00000000..d5f0f704 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02a/src/sub1.f90 @@ -0,0 +1,6 @@ +subroutine sub1() +implicit none + + print*,"haha1" + +end subroutine sub1 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02a/src/sub2.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02a/src/sub2.f90 new file mode 100644 index 00000000..a8fd6f8a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02a/src/sub2.f90 @@ -0,0 +1,6 @@ +subroutine sub2() +implicit none + + print*,"haha2" + +end subroutine sub2 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02a/test/main.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02a/test/main.f90 new file mode 100644 index 00000000..77996d47 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02a/test/main.f90 @@ -0,0 +1,7 @@ +program main_prog +implicit none + + call sub1 + call sub2 + +end program main_prog \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02b/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/02b/CMakeLists.txt new file mode 100644 index 00000000..a7a946bf --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02b/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.20) + +project ( testprj ) + +set ( PRJ_COMPILE_FEATURES ) +set ( PRJ_COMPILE_DEFINITIONS ) + +enable_language(Fortran) + +list ( APPEND PRJ_COMPILE_FEATURES cxx_std_20 ) + +#add_subdirectory(src) +add_subdirectory(tests) + + +#target_compile_features ( ${PROJECT_NAME} +# PRIVATE +# ${PRJ_COMPILE_FEATURES} +#) + +#target_compile_definitions ( ${PROJECT_NAME} +# PRIVATE +# ${PRJ_COMPILE_DEFINITIONS} +#) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02b/src/sub1.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02b/src/sub1.f90 new file mode 100644 index 00000000..d5f0f704 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02b/src/sub1.f90 @@ -0,0 +1,6 @@ +subroutine sub1() +implicit none + + print*,"haha1" + +end subroutine sub1 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02b/src/sub2.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02b/src/sub2.f90 new file mode 100644 index 00000000..a8fd6f8a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02b/src/sub2.f90 @@ -0,0 +1,6 @@ +subroutine sub2() +implicit none + + print*,"haha2" + +end subroutine sub2 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02b/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/02b/tests/CMakeLists.txt new file mode 100644 index 00000000..47caf173 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02b/tests/CMakeLists.txt @@ -0,0 +1,16 @@ + +add_executable( ${PROJECT_NAME} + main.f90 + ../src/sub1.f90 + ../src/sub2.f90 +) + +target_compile_features ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02b/tests/main.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02b/tests/main.f90 new file mode 100644 index 00000000..77996d47 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02b/tests/main.f90 @@ -0,0 +1,7 @@ +program main_prog +implicit none + + call sub1 + call sub2 + +end program main_prog \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02c/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/02c/CMakeLists.txt new file mode 100644 index 00000000..af747beb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02c/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.20) + +project ( testprj ) + +set ( PRJ_COMPILE_FEATURES ) +set ( PRJ_COMPILE_DEFINITIONS ) + +enable_language(Fortran) + +list ( APPEND PRJ_COMPILE_FEATURES cxx_std_20 ) + +add_subdirectory(tests) +add_subdirectory(test1) + + diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02c/src/sub1.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02c/src/sub1.f90 new file mode 100644 index 00000000..d5f0f704 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02c/src/sub1.f90 @@ -0,0 +1,6 @@ +subroutine sub1() +implicit none + + print*,"haha1" + +end subroutine sub1 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02c/src/sub2.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02c/src/sub2.f90 new file mode 100644 index 00000000..a8fd6f8a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02c/src/sub2.f90 @@ -0,0 +1,6 @@ +subroutine sub2() +implicit none + + print*,"haha2" + +end subroutine sub2 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02c/test1/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/02c/test1/CMakeLists.txt new file mode 100644 index 00000000..c3f2b959 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02c/test1/CMakeLists.txt @@ -0,0 +1,16 @@ + +add_executable( main1 + main1.f90 + ../src/sub1.f90 + ../src/sub2.f90 +) + +target_compile_features ( main1 + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( main1 + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02c/test1/main1.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02c/test1/main1.f90 new file mode 100644 index 00000000..44866f7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02c/test1/main1.f90 @@ -0,0 +1,8 @@ +program main_prog1 +implicit none + + call sub1 + call sub2 + print*,"hahaha" + +end program main_prog1 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02c/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/02c/tests/CMakeLists.txt new file mode 100644 index 00000000..47caf173 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02c/tests/CMakeLists.txt @@ -0,0 +1,16 @@ + +add_executable( ${PROJECT_NAME} + main.f90 + ../src/sub1.f90 + ../src/sub2.f90 +) + +target_compile_features ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02c/tests/main.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02c/tests/main.f90 new file mode 100644 index 00000000..77996d47 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02c/tests/main.f90 @@ -0,0 +1,7 @@ +program main_prog +implicit none + + call sub1 + call sub2 + +end program main_prog \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02d/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/02d/CMakeLists.txt new file mode 100644 index 00000000..9e8feea4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02d/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.20) + +project ( testprj ) + +set ( PRJ_COMPILE_FEATURES ) +set ( PRJ_COMPILE_DEFINITIONS ) + +enable_language(Fortran) + +list ( APPEND PRJ_COMPILE_FEATURES cxx_std_20 ) + +add_subdirectory(src) +add_subdirectory(tests) + + + diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02d/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/02d/src/CMakeLists.txt new file mode 100644 index 00000000..86dc91bd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02d/src/CMakeLists.txt @@ -0,0 +1,18 @@ + +set(mylibname "mylib" ) + +add_library(${mylibname} STATIC + sub1.f90 + sub2.f90 +) + + +target_compile_features ( ${mylibname} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${mylibname} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02d/src/sub1.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02d/src/sub1.f90 new file mode 100644 index 00000000..d5f0f704 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02d/src/sub1.f90 @@ -0,0 +1,6 @@ +subroutine sub1() +implicit none + + print*,"haha1" + +end subroutine sub1 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02d/src/sub2.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02d/src/sub2.f90 new file mode 100644 index 00000000..a8fd6f8a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02d/src/sub2.f90 @@ -0,0 +1,6 @@ +subroutine sub2() +implicit none + + print*,"haha2" + +end subroutine sub2 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02d/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/02d/tests/CMakeLists.txt new file mode 100644 index 00000000..16e89309 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02d/tests/CMakeLists.txt @@ -0,0 +1,23 @@ + +message(STATUS "CMAKE_BINARY_DIR=${CMAKE_BINARY_DIR}") +message(STATUS "CMAKE_BINARY_DIR/src/Debug/mylib.lib=${CMAKE_BINARY_DIR}/src/Debug/mylib.lib") + + +add_executable( ${PROJECT_NAME} + main.f90 +) + +target_compile_features ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) + +target_link_libraries( ${PROJECT_NAME} + PRIVATE + ${CMAKE_BINARY_DIR}/src/Debug/mylib.lib +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02d/tests/main.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02d/tests/main.f90 new file mode 100644 index 00000000..77996d47 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02d/tests/main.f90 @@ -0,0 +1,7 @@ +program main_prog +implicit none + + call sub1 + call sub2 + +end program main_prog \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02e/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/02e/CMakeLists.txt new file mode 100644 index 00000000..9e8feea4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02e/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.20) + +project ( testprj ) + +set ( PRJ_COMPILE_FEATURES ) +set ( PRJ_COMPILE_DEFINITIONS ) + +enable_language(Fortran) + +list ( APPEND PRJ_COMPILE_FEATURES cxx_std_20 ) + +add_subdirectory(src) +add_subdirectory(tests) + + + diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02e/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/02e/src/CMakeLists.txt new file mode 100644 index 00000000..86dc91bd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02e/src/CMakeLists.txt @@ -0,0 +1,18 @@ + +set(mylibname "mylib" ) + +add_library(${mylibname} STATIC + sub1.f90 + sub2.f90 +) + + +target_compile_features ( ${mylibname} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${mylibname} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02e/src/sub1.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02e/src/sub1.f90 new file mode 100644 index 00000000..d5f0f704 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02e/src/sub1.f90 @@ -0,0 +1,6 @@ +subroutine sub1() +implicit none + + print*,"haha1" + +end subroutine sub1 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02e/src/sub2.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02e/src/sub2.f90 new file mode 100644 index 00000000..a8fd6f8a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02e/src/sub2.f90 @@ -0,0 +1,6 @@ +subroutine sub2() +implicit none + + print*,"haha2" + +end subroutine sub2 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02e/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/02e/tests/CMakeLists.txt new file mode 100644 index 00000000..51643614 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02e/tests/CMakeLists.txt @@ -0,0 +1,19 @@ + +add_executable( ${PROJECT_NAME} + main.f90 +) + +target_compile_features ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) + +target_link_libraries( ${PROJECT_NAME} + PRIVATE + mylib +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02e/tests/main.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02e/tests/main.f90 new file mode 100644 index 00000000..77996d47 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02e/tests/main.f90 @@ -0,0 +1,7 @@ +program main_prog +implicit none + + call sub1 + call sub2 + +end program main_prog \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02f/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/02f/CMakeLists.txt new file mode 100644 index 00000000..4bab2a8d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02f/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.20) + +project ( testprj ) + +set ( PRJ_COMPILE_FEATURES ) +set ( PRJ_COMPILE_DEFINITIONS ) + +enable_language(Fortran) + +list ( APPEND PRJ_COMPILE_FEATURES cxx_std_20 ) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) + + + diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02f/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/02f/src/CMakeLists.txt new file mode 100644 index 00000000..86dc91bd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02f/src/CMakeLists.txt @@ -0,0 +1,18 @@ + +set(mylibname "mylib" ) + +add_library(${mylibname} STATIC + sub1.f90 + sub2.f90 +) + + +target_compile_features ( ${mylibname} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${mylibname} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02f/src/sub1.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02f/src/sub1.f90 new file mode 100644 index 00000000..d5f0f704 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02f/src/sub1.f90 @@ -0,0 +1,6 @@ +subroutine sub1() +implicit none + + print*,"haha1" + +end subroutine sub1 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02f/src/sub2.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02f/src/sub2.f90 new file mode 100644 index 00000000..a8fd6f8a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02f/src/sub2.f90 @@ -0,0 +1,6 @@ +subroutine sub2() +implicit none + + print*,"haha2" + +end subroutine sub2 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02f/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/02f/tests/CMakeLists.txt new file mode 100644 index 00000000..51643614 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02f/tests/CMakeLists.txt @@ -0,0 +1,19 @@ + +add_executable( ${PROJECT_NAME} + main.f90 +) + +target_compile_features ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) + +target_link_libraries( ${PROJECT_NAME} + PRIVATE + mylib +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02f/tests/main.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02f/tests/main.f90 new file mode 100644 index 00000000..77996d47 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02f/tests/main.f90 @@ -0,0 +1,7 @@ +program main_prog +implicit none + + call sub1 + call sub2 + +end program main_prog \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02g/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/02g/CMakeLists.txt new file mode 100644 index 00000000..44d1f730 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02g/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.20) + +project ( testprj ) + +set ( PRJ_COMPILE_FEATURES ) +set ( PRJ_COMPILE_DEFINITIONS ) + +enable_language(Fortran) + +list ( APPEND PRJ_COMPILE_FEATURES cxx_std_20 ) + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) + + + diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02g/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/02g/src/CMakeLists.txt new file mode 100644 index 00000000..86dc91bd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02g/src/CMakeLists.txt @@ -0,0 +1,18 @@ + +set(mylibname "mylib" ) + +add_library(${mylibname} STATIC + sub1.f90 + sub2.f90 +) + + +target_compile_features ( ${mylibname} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${mylibname} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02g/src/sub1.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02g/src/sub1.f90 new file mode 100644 index 00000000..d5f0f704 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02g/src/sub1.f90 @@ -0,0 +1,6 @@ +subroutine sub1() +implicit none + + print*,"haha1" + +end subroutine sub1 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02g/src/sub2.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02g/src/sub2.f90 new file mode 100644 index 00000000..a8fd6f8a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02g/src/sub2.f90 @@ -0,0 +1,6 @@ +subroutine sub2() +implicit none + + print*,"haha2" + +end subroutine sub2 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02g/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/02g/tests/CMakeLists.txt new file mode 100644 index 00000000..3f951d2c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02g/tests/CMakeLists.txt @@ -0,0 +1,24 @@ + +add_executable( ${PROJECT_NAME} + main.f90 +) + +target_include_directories( ${PROJECT_NAME} + PRIVATE + ${CMAKE_BINARY_DIR}/modules +) + +target_compile_features ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) + +target_link_libraries( ${PROJECT_NAME} + PRIVATE + mylib +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02g/tests/main.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02g/tests/main.f90 new file mode 100644 index 00000000..77996d47 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02g/tests/main.f90 @@ -0,0 +1,7 @@ +program main_prog +implicit none + + call sub1 + call sub2 + +end program main_prog \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02h/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/02h/CMakeLists.txt new file mode 100644 index 00000000..44d1f730 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02h/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.20) + +project ( testprj ) + +set ( PRJ_COMPILE_FEATURES ) +set ( PRJ_COMPILE_DEFINITIONS ) + +enable_language(Fortran) + +list ( APPEND PRJ_COMPILE_FEATURES cxx_std_20 ) + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) + + + diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02h/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/02h/src/CMakeLists.txt new file mode 100644 index 00000000..e5543637 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02h/src/CMakeLists.txt @@ -0,0 +1,22 @@ + +set(mylibname "mylib" ) + +add_library(${mylibname} STATIC + sub1.f90 + sub2.f90 +) + +target_include_directories( ${mylibname} + PRIVATE + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +target_compile_features ( ${mylibname} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${mylibname} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02h/src/sub1.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02h/src/sub1.f90 new file mode 100644 index 00000000..d5f0f704 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02h/src/sub1.f90 @@ -0,0 +1,6 @@ +subroutine sub1() +implicit none + + print*,"haha1" + +end subroutine sub1 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02h/src/sub2.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02h/src/sub2.f90 new file mode 100644 index 00000000..a8fd6f8a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02h/src/sub2.f90 @@ -0,0 +1,6 @@ +subroutine sub2() +implicit none + + print*,"haha2" + +end subroutine sub2 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02h/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/02h/tests/CMakeLists.txt new file mode 100644 index 00000000..0e4eaabf --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02h/tests/CMakeLists.txt @@ -0,0 +1,24 @@ + +add_executable( ${PROJECT_NAME} + main.f90 +) + +target_include_directories( ${PROJECT_NAME} + PRIVATE + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +target_compile_features ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) + +target_link_libraries( ${PROJECT_NAME} + PRIVATE + mylib +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/02h/tests/main.f90 b/example/1d-linear-convection/weno3/fortran/grammar/02h/tests/main.f90 new file mode 100644 index 00000000..77996d47 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/02h/tests/main.f90 @@ -0,0 +1,7 @@ +program main_prog +implicit none + + call sub1 + call sub2 + +end program main_prog \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/03/CMakeLists.txt new file mode 100644 index 00000000..39f49ea9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03/CMakeLists.txt @@ -0,0 +1,38 @@ +cmake_minimum_required(VERSION 3.20) + +project ( testprj ) + +set ( PRJ_COMPILE_FEATURES ) +set ( PRJ_COMPILE_DEFINITIONS ) + +enable_language(Fortran) + +list ( APPEND PRJ_COMPILE_FEATURES cxx_std_20 ) + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_executable( ${PROJECT_NAME} + main.f90 +) + +target_include_directories( ${PROJECT_NAME} + PRIVATE + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +target_compile_features ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) + + + + diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03/main.f90 b/example/1d-linear-convection/weno3/fortran/grammar/03/main.f90 new file mode 100644 index 00000000..5d628147 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03/main.f90 @@ -0,0 +1,33 @@ +module mymod1 +implicit none + + integer, parameter :: N = 1024 + +contains + subroutine show_N() + print*, "N = ", N + end subroutine show_N + +end module mymod1 + +module mymod2 +implicit none + + integer, parameter :: M = 256 + +contains + subroutine show_M() + print*, "M = ", M + end subroutine show_M + +end module mymod2 + +program main_prog +use mymod1 +use mymod2 +implicit none + + call show_N() + call show_M() + +end program main_prog \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03a/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/03a/CMakeLists.txt new file mode 100644 index 00000000..687388f8 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03a/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.20) + +project ( testprj ) + +set ( PRJ_COMPILE_FEATURES ) +set ( PRJ_COMPILE_DEFINITIONS ) + +enable_language(Fortran) + +list ( APPEND PRJ_COMPILE_FEATURES cxx_std_20 ) + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_executable( ${PROJECT_NAME} + main.f90 + mymod1.f90 + mymod2.f90 +) + +target_include_directories( ${PROJECT_NAME} + PRIVATE + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +target_compile_features ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) + + + + diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03a/main.f90 b/example/1d-linear-convection/weno3/fortran/grammar/03a/main.f90 new file mode 100644 index 00000000..5d9d514a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03a/main.f90 @@ -0,0 +1,9 @@ +program main_prog +use mymod1 +use mymod2 +implicit none + + call show_N() + call show_M() + +end program main_prog \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03a/mymod1.f90 b/example/1d-linear-convection/weno3/fortran/grammar/03a/mymod1.f90 new file mode 100644 index 00000000..49f99c96 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03a/mymod1.f90 @@ -0,0 +1,11 @@ +module mymod1 +implicit none + + integer, parameter :: N = 1024 + +contains + subroutine show_N() + print*, "N = ", N + end subroutine show_N + +end module mymod1 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03a/mymod2.f90 b/example/1d-linear-convection/weno3/fortran/grammar/03a/mymod2.f90 new file mode 100644 index 00000000..b265eeb9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03a/mymod2.f90 @@ -0,0 +1,11 @@ +module mymod2 +implicit none + + integer, parameter :: M = 256 + +contains + subroutine show_M() + print*, "M = ", M + end subroutine show_M + +end module mymod2 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03b/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/03b/CMakeLists.txt new file mode 100644 index 00000000..25ba498c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03b/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.20) + +project ( testprj ) + +set ( PRJ_COMPILE_FEATURES ) +set ( PRJ_COMPILE_DEFINITIONS ) + +enable_language(Fortran) + +list ( APPEND PRJ_COMPILE_FEATURES cxx_std_20 ) + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_executable( ${PROJECT_NAME} + main.f90 + src/mymod1.f90 + src/mymod2.f90 +) + +target_include_directories( ${PROJECT_NAME} + PRIVATE + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +target_compile_features ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) + + + + diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03b/main.f90 b/example/1d-linear-convection/weno3/fortran/grammar/03b/main.f90 new file mode 100644 index 00000000..5d9d514a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03b/main.f90 @@ -0,0 +1,9 @@ +program main_prog +use mymod1 +use mymod2 +implicit none + + call show_N() + call show_M() + +end program main_prog \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03b/src/mymod1.f90 b/example/1d-linear-convection/weno3/fortran/grammar/03b/src/mymod1.f90 new file mode 100644 index 00000000..49f99c96 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03b/src/mymod1.f90 @@ -0,0 +1,11 @@ +module mymod1 +implicit none + + integer, parameter :: N = 1024 + +contains + subroutine show_N() + print*, "N = ", N + end subroutine show_N + +end module mymod1 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03b/src/mymod2.f90 b/example/1d-linear-convection/weno3/fortran/grammar/03b/src/mymod2.f90 new file mode 100644 index 00000000..b265eeb9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03b/src/mymod2.f90 @@ -0,0 +1,11 @@ +module mymod2 +implicit none + + integer, parameter :: M = 256 + +contains + subroutine show_M() + print*, "M = ", M + end subroutine show_M + +end module mymod2 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03c/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/03c/CMakeLists.txt new file mode 100644 index 00000000..477aa6e4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03c/CMakeLists.txt @@ -0,0 +1,47 @@ +cmake_minimum_required(VERSION 3.20) + +project ( testprj ) + +set ( PRJ_COMPILE_FEATURES ) +set ( PRJ_COMPILE_DEFINITIONS ) +set ( PRJ_LIBRARIES ) + +enable_language(Fortran) + +list ( APPEND PRJ_COMPILE_FEATURES cxx_std_20 ) +list ( APPEND PRJ_LIBRARIES mylib ) + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) + +add_executable( ${PROJECT_NAME} + main.f90 +) + +target_include_directories( ${PROJECT_NAME} + PRIVATE + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +target_compile_features ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) + +target_link_libraries( ${PROJECT_NAME} + PRIVATE + ${PRJ_LIBRARIES} +) + + + + diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03c/main.f90 b/example/1d-linear-convection/weno3/fortran/grammar/03c/main.f90 new file mode 100644 index 00000000..5d9d514a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03c/main.f90 @@ -0,0 +1,9 @@ +program main_prog +use mymod1 +use mymod2 +implicit none + + call show_N() + call show_M() + +end program main_prog \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03c/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/03c/src/CMakeLists.txt new file mode 100644 index 00000000..1962a868 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03c/src/CMakeLists.txt @@ -0,0 +1,22 @@ + +set(mylibname "mylib" ) + +add_library(${mylibname} STATIC + mymod1.f90 + mymod2.f90 +) + +target_include_directories( ${mylibname} + PRIVATE + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +target_compile_features ( ${mylibname} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${mylibname} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03c/src/mymod1.f90 b/example/1d-linear-convection/weno3/fortran/grammar/03c/src/mymod1.f90 new file mode 100644 index 00000000..49f99c96 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03c/src/mymod1.f90 @@ -0,0 +1,11 @@ +module mymod1 +implicit none + + integer, parameter :: N = 1024 + +contains + subroutine show_N() + print*, "N = ", N + end subroutine show_N + +end module mymod1 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03c/src/mymod2.f90 b/example/1d-linear-convection/weno3/fortran/grammar/03c/src/mymod2.f90 new file mode 100644 index 00000000..b265eeb9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03c/src/mymod2.f90 @@ -0,0 +1,11 @@ +module mymod2 +implicit none + + integer, parameter :: M = 256 + +contains + subroutine show_M() + print*, "M = ", M + end subroutine show_M + +end module mymod2 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03d/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/03d/CMakeLists.txt new file mode 100644 index 00000000..97bcbb03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03d/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.20) + +project ( testprj ) + +set ( PRJ_COMPILE_FEATURES ) +set ( PRJ_COMPILE_DEFINITIONS ) +set ( PRJ_LIBRARIES ) + +enable_language(Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +list ( APPEND PRJ_COMPILE_FEATURES cxx_std_20 ) +list ( APPEND PRJ_LIBRARIES mylib ) + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03d/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/03d/src/CMakeLists.txt new file mode 100644 index 00000000..afa33111 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03d/src/CMakeLists.txt @@ -0,0 +1,22 @@ + +set(mylibname "mylib" ) + +add_library(${mylibname} STATIC + mymod1.f90 + mymod2.f90 +) + +#target_include_directories( ${mylibname} +# PRIVATE +# ${CMAKE_Fortran_MODULE_DIRECTORY} +#) + +#target_compile_features ( ${mylibname} +# PRIVATE +# ${PRJ_COMPILE_FEATURES} +#) + +#target_compile_definitions ( ${mylibname} +# PRIVATE +# ${PRJ_COMPILE_DEFINITIONS} +#) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03d/src/mymod1.f90 b/example/1d-linear-convection/weno3/fortran/grammar/03d/src/mymod1.f90 new file mode 100644 index 00000000..49f99c96 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03d/src/mymod1.f90 @@ -0,0 +1,11 @@ +module mymod1 +implicit none + + integer, parameter :: N = 1024 + +contains + subroutine show_N() + print*, "N = ", N + end subroutine show_N + +end module mymod1 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03d/src/mymod2.f90 b/example/1d-linear-convection/weno3/fortran/grammar/03d/src/mymod2.f90 new file mode 100644 index 00000000..b265eeb9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03d/src/mymod2.f90 @@ -0,0 +1,11 @@ +module mymod2 +implicit none + + integer, parameter :: M = 256 + +contains + subroutine show_M() + print*, "M = ", M + end subroutine show_M + +end module mymod2 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03d/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/03d/tests/CMakeLists.txt new file mode 100644 index 00000000..7658d2eb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03d/tests/CMakeLists.txt @@ -0,0 +1,23 @@ +add_executable( ${PROJECT_NAME} + main.f90 +) + +#target_include_directories( ${PROJECT_NAME} +# PRIVATE +# ${CMAKE_Fortran_MODULE_DIRECTORY} +#) + +target_compile_features ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) + +target_link_libraries( ${PROJECT_NAME} + PRIVATE + ${PRJ_LIBRARIES} +) diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03d/tests/main.f90 b/example/1d-linear-convection/weno3/fortran/grammar/03d/tests/main.f90 new file mode 100644 index 00000000..5d9d514a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03d/tests/main.f90 @@ -0,0 +1,9 @@ +program main_prog +use mymod1 +use mymod2 +implicit none + + call show_N() + call show_M() + +end program main_prog \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03e/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/03e/CMakeLists.txt new file mode 100644 index 00000000..97bcbb03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03e/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.20) + +project ( testprj ) + +set ( PRJ_COMPILE_FEATURES ) +set ( PRJ_COMPILE_DEFINITIONS ) +set ( PRJ_LIBRARIES ) + +enable_language(Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +list ( APPEND PRJ_COMPILE_FEATURES cxx_std_20 ) +list ( APPEND PRJ_LIBRARIES mylib ) + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03e/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/03e/src/CMakeLists.txt new file mode 100644 index 00000000..1983bbf5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03e/src/CMakeLists.txt @@ -0,0 +1,21 @@ + +set(mylibname "mylib" ) + +add_library(${mylibname} STATIC + mymath.f90 +) + +#target_include_directories( ${mylibname} +# PRIVATE +# ${CMAKE_Fortran_MODULE_DIRECTORY} +#) + +#target_compile_features ( ${mylibname} +# PRIVATE +# ${PRJ_COMPILE_FEATURES} +#) + +#target_compile_definitions ( ${mylibname} +# PRIVATE +# ${PRJ_COMPILE_DEFINITIONS} +#) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03e/src/mymath.f90 b/example/1d-linear-convection/weno3/fortran/grammar/03e/src/mymath.f90 new file mode 100644 index 00000000..979187cf --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03e/src/mymath.f90 @@ -0,0 +1,10 @@ +module mymath_module + implicit none + integer, parameter :: N = 1024 + +contains + subroutine show_N() + print*, "N = ", N + end subroutine show_N + +end module mymath_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03e/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/03e/tests/CMakeLists.txt new file mode 100644 index 00000000..7658d2eb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03e/tests/CMakeLists.txt @@ -0,0 +1,23 @@ +add_executable( ${PROJECT_NAME} + main.f90 +) + +#target_include_directories( ${PROJECT_NAME} +# PRIVATE +# ${CMAKE_Fortran_MODULE_DIRECTORY} +#) + +target_compile_features ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) + +target_link_libraries( ${PROJECT_NAME} + PRIVATE + ${PRJ_LIBRARIES} +) diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03e/tests/main.f90 b/example/1d-linear-convection/weno3/fortran/grammar/03e/tests/main.f90 new file mode 100644 index 00000000..ffc5b2b7 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03e/tests/main.f90 @@ -0,0 +1,33 @@ +! 测试程序:调用mymath库的函数,验证功能 +program test_mymath + use iso_fortran_env, only: real64 ! 显式引入,避免模块依赖缺失导致的real64未定义 + use mymath_module + implicit none + real(real64) :: a, b, sum_res, mul_res + + ! 测试数据 + a = 2.5_real64 + b = 4.0_real64 + + ! 调用库函数 + sum_res = add(a, b) + mul_res = multiply(a, b) + + ! 输出结果 + print *, "=== Testing mymath library ===" + print *, "a = ", a + print *, "b = ", b + print *, "a + b = ", sum_res + print *, "a * b = ", mul_res + print *, "=== Test completed ===" + + ! 简单验证(确保结果正确) + if (abs(sum_res - 6.5_real64) < 1e-10_real64 .and. & + abs(mul_res - 10.0_real64) < 1e-10_real64) then + print *, "✓ Test passed!" + else + print *, "✗ Test failed!" + stop 1 + end if + +end program test_mymath \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03f/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/03f/CMakeLists.txt new file mode 100644 index 00000000..97bcbb03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03f/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.20) + +project ( testprj ) + +set ( PRJ_COMPILE_FEATURES ) +set ( PRJ_COMPILE_DEFINITIONS ) +set ( PRJ_LIBRARIES ) + +enable_language(Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +list ( APPEND PRJ_COMPILE_FEATURES cxx_std_20 ) +list ( APPEND PRJ_LIBRARIES mylib ) + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03f/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/03f/src/CMakeLists.txt new file mode 100644 index 00000000..1983bbf5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03f/src/CMakeLists.txt @@ -0,0 +1,21 @@ + +set(mylibname "mylib" ) + +add_library(${mylibname} STATIC + mymath.f90 +) + +#target_include_directories( ${mylibname} +# PRIVATE +# ${CMAKE_Fortran_MODULE_DIRECTORY} +#) + +#target_compile_features ( ${mylibname} +# PRIVATE +# ${PRJ_COMPILE_FEATURES} +#) + +#target_compile_definitions ( ${mylibname} +# PRIVATE +# ${PRJ_COMPILE_DEFINITIONS} +#) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03f/src/mymath.f90 b/example/1d-linear-convection/weno3/fortran/grammar/03f/src/mymath.f90 new file mode 100644 index 00000000..11d02043 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03f/src/mymath.f90 @@ -0,0 +1,24 @@ +! 简单的数学库:提供加法和乘法函数 +module mymath_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + private + public :: add, multiply + +contains + + ! 实数加法 + function add(a, b) result(res) + real(real64), intent(in) :: a, b + real(real64) :: res + res = a + b + end function add + + ! 实数乘法 + function multiply(a, b) result(res) + real(real64), intent(in) :: a, b + real(real64) :: res + res = a * b + end function multiply + +end module mymath_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03f/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/grammar/03f/tests/CMakeLists.txt new file mode 100644 index 00000000..7658d2eb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03f/tests/CMakeLists.txt @@ -0,0 +1,23 @@ +add_executable( ${PROJECT_NAME} + main.f90 +) + +#target_include_directories( ${PROJECT_NAME} +# PRIVATE +# ${CMAKE_Fortran_MODULE_DIRECTORY} +#) + +target_compile_features ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_FEATURES} +) + +target_compile_definitions ( ${PROJECT_NAME} + PRIVATE + ${PRJ_COMPILE_DEFINITIONS} +) + +target_link_libraries( ${PROJECT_NAME} + PRIVATE + ${PRJ_LIBRARIES} +) diff --git a/example/1d-linear-convection/weno3/fortran/grammar/03f/tests/main.f90 b/example/1d-linear-convection/weno3/fortran/grammar/03f/tests/main.f90 new file mode 100644 index 00000000..ffc5b2b7 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/grammar/03f/tests/main.f90 @@ -0,0 +1,33 @@ +! 测试程序:调用mymath库的函数,验证功能 +program test_mymath + use iso_fortran_env, only: real64 ! 显式引入,避免模块依赖缺失导致的real64未定义 + use mymath_module + implicit none + real(real64) :: a, b, sum_res, mul_res + + ! 测试数据 + a = 2.5_real64 + b = 4.0_real64 + + ! 调用库函数 + sum_res = add(a, b) + mul_res = multiply(a, b) + + ! 输出结果 + print *, "=== Testing mymath library ===" + print *, "a = ", a + print *, "b = ", b + print *, "a + b = ", sum_res + print *, "a * b = ", mul_res + print *, "=== Test completed ===" + + ! 简单验证(确保结果正确) + if (abs(sum_res - 6.5_real64) < 1e-10_real64 .and. & + abs(mul_res - 10.0_real64) < 1e-10_real64) then + print *, "✓ Test passed!" + else + print *, "✗ Test failed!" + stop 1 + end if + +end program test_mymath \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01/CMakeLists.txt new file mode 100644 index 00000000..665f48ac --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01/CMakeLists.txt @@ -0,0 +1,26 @@ +# 根目录CMakeLists.txt +#cmake_minimum_required(VERSION 3.12) +cmake_minimum_required(VERSION 4.2.1) +project(FortranRegistry VERSION 1.0.0 LANGUAGES Fortran) + +message(STATUS "CMAKE_Fortran_COMPILER=${CMAKE_Fortran_COMPILER}") +message(STATUS "CMAKE_Fortran_COMPILER_ID=${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "CMAKE_Fortran_COMPILER_VERSION=${CMAKE_Fortran_COMPILER_VERSION}") + +# 设置Fortran标准 +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +# 模块输出目录 +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) + +# 编译器选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "GNU") + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -Wall -Wextra") + set(CMAKE_Fortran_FLAGS_DEBUG "-g -O0 -fcheck=all -fbacktrace") + set(CMAKE_Fortran_FLAGS_RELEASE "-O3") +endif() + +# 添加子目录 +add_subdirectory(src) +add_subdirectory(tests) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01/src/CMakeLists.txt new file mode 100644 index 00000000..0ddd08d2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01/src/CMakeLists.txt @@ -0,0 +1,19 @@ +# src/CMakeLists.txt +# 源代码目录的CMake配置 + +message(STATUS "配置源代码目录...") + +# 添加核心模块子目录 +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/core AND + EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/core/CMakeLists.txt) + add_subdirectory(core) +else() + message(WARNING "core目录或CMakeLists.txt不存在") +endif() + +# 可选:添加其他模块子目录 +# if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/infrastructure) +# add_subdirectory(infrastructure) +# endif() + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01/src/core/CMakeLists.txt new file mode 100644 index 00000000..9685b6fb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01/src/core/CMakeLists.txt @@ -0,0 +1,16 @@ +# src/core/CMakeLists.txt +# 创建核心库 +add_library(core + registry.f90 +) + +# 设置模块输出目录 +target_include_directories(core PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 安装规则(可选) +install(TARGETS core + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/01/src/core/registry.f90 new file mode 100644 index 00000000..09aced0b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01/src/core/registry.f90 @@ -0,0 +1,187 @@ +! src/core/registry.f90 +! Fortran注册系统模块 +module registry_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private ! 默认所有内容都是私有的 + + ! ==================== 1. 公开接口 ==================== + ! 只在这里声明一次哪些是公开的 + public :: wp ! 精度类型 + public :: component_info ! 组件信息类型 + public :: component_registry_type ! 注册表类型 + public :: component_registry ! 全局注册表实例 + public :: register_component ! 注册函数 + + ! ==================== 2. 类型定义 ==================== + + ! 组件信息类型 + type :: component_info + character(len=50) :: category = "" + character(len=50) :: name = "" + contains + procedure :: print => ci_print + end type component_info + + ! 注册表类型 + type :: component_registry_type + private + type(component_info), allocatable :: components(:) + integer :: count = 0 + integer :: capacity = 10 + logical :: verbose = .true. + contains + procedure :: register => cr_register + procedure :: get => cr_get + procedure :: list_all => cr_list_all + procedure :: size => cr_size + procedure :: clear => cr_clear + end type component_registry_type + + ! ==================== 3. 全局实例 ==================== + ! 这里只声明,不指定属性 + type(component_registry_type) :: component_registry + + ! ==================== 4. 接口定义 ==================== + + interface register_component + module procedure register_component_simple + end interface register_component + +contains + + ! ==================== 5. 组件信息方法 ==================== + + subroutine ci_print(this) + class(component_info), intent(in) :: this + print *, " [", trim(this%category), ".", trim(this%name), "]" + end subroutine ci_print + + ! ==================== 6. 注册表核心方法 ==================== + + ! 注册组件(简单版) + subroutine register_component_simple(category, name) + character(len=*), intent(in) :: category, name + call component_registry%register(category, name) + end subroutine register_component_simple + + ! 内部注册实现 + subroutine cr_register(this, category, name) + class(component_registry_type), intent(inout) :: this + character(len=*), intent(in) :: category, name + + type(component_info) :: new_component + type(component_info), allocatable :: temp(:) + integer :: i + + ! 检查是否已存在 + do i = 1, this%count + if (trim(this%components(i)%category) == trim(category) .and. & + trim(this%components(i)%name) == trim(name)) then + if (this%verbose) then + print *, "⚠️ 警告: 覆盖注册 ", trim(category), ".", trim(name) + end if + ! 更新现有组件 + this%components(i)%category = trim(category) + this%components(i)%name = trim(name) + return + end if + end do + + ! 创建新组件 + new_component%category = trim(category) + new_component%name = trim(name) + + ! 初始化数组(如果需要) + if (.not. allocated(this%components)) then + allocate(this%components(this%capacity)) + end if + + ! 扩展数组(如果需要) + if (this%count >= this%capacity) then + this%capacity = this%capacity * 2 + allocate(temp(this%capacity)) + temp(1:this%count) = this%components(1:this%count) + call move_alloc(temp, this%components) + end if + + ! 添加组件 + this%count = this%count + 1 + this%components(this%count) = new_component + + if (this%verbose) then + print *, "✅ 已注册: ", trim(category), ".", trim(name) + end if + end subroutine cr_register + + ! 获取组件 + function cr_get(this, category, name) result(info) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + type(component_info) :: info + + integer :: i + + ! 初始化为空 + info%category = "" + info%name = "" + + ! 搜索组件 + do i = 1, this%count + if (trim(this%components(i)%category) == trim(category) .and. & + trim(this%components(i)%name) == trim(name)) then + info = this%components(i) + return + end if + end do + + if (this%verbose) then + print *, "❌ 未找到: ", trim(category), ".", trim(name) + end if + end function cr_get + + ! 列出所有组件 + subroutine cr_list_all(this) + class(component_registry_type), intent(in) :: this + integer :: i + + print *, "=== 注册表内容 (" // trim(str(this%count)) // " 个组件) ===" + if (this%count == 0) then + print *, " (空)" + return + end if + + do i = 1, this%count + call this%components(i)%print() + end do + end subroutine cr_list_all + + ! 获取组件数量 + integer function cr_size(this) + class(component_registry_type), intent(in) :: this + cr_size = this%count + end function cr_size + + ! 清空注册表 + subroutine cr_clear(this) + class(component_registry_type), intent(inout) :: this + if (allocated(this%components)) then + deallocate(this%components) + end if + this%count = 0 + this%capacity = 10 + print *, "🗑️ 注册表已清空" + end subroutine cr_clear + + ! ==================== 7. 工具函数 ==================== + + ! 整数转字符串 + function str(i) result(s) + integer, intent(in) :: i + character(len=20) :: s + write(s, '(I0)') i + s = trim(adjustl(s)) + end function str + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01/tests/CMakeLists.txt new file mode 100644 index 00000000..141f8045 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01/tests/CMakeLists.txt @@ -0,0 +1,15 @@ +# tests/CMakeLists.txt +# 创建测试程序 +add_executable(test_registry + test_registry.f90 +) + +# 链接核心库 +target_link_libraries(test_registry + core +) + +# 设置模块路径 +target_include_directories(test_registry PRIVATE + ${CMAKE_Fortran_MODULE_DIRECTORY} +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01/tests/test_registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/01/tests/test_registry.f90 new file mode 100644 index 00000000..9c770d76 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01/tests/test_registry.f90 @@ -0,0 +1,65 @@ +! tests/test_registry.f90 +program test_registry + use registry_module + implicit none + + type(component_info) :: info + integer :: initial_size + + print *, "=== Fortran注册系统测试 ===" + print *, "" + + ! 测试1: 基本注册 + print *, "测试1: 基本注册功能" + print *, "---------------------" + + call component_registry%list_all() + initial_size = component_registry%size() + print *, "初始大小: ", initial_size + + ! 注册一些组件 + call register_component("reconstructor", "eno") + call register_component("reconstructor", "weno3") + call register_component("reconstructor", "weno5") + call register_component("flux", "rusanov") + call register_component("flux", "engquist-osher") + call register_component("integrator", "rk1") + call register_component("integrator", "rk2") + call register_component("integrator", "rk3") + + print *, "注册后大小: ", component_registry%size() + call component_registry%list_all() + print *, "" + + ! 测试2: 重复注册 + print *, "测试2: 重复注册(应该显示警告)" + print *, "-------------------------------" + call register_component("reconstructor", "eno") + print *, "" + + ! 测试3: 获取组件 + print *, "测试3: 获取组件功能" + print *, "-------------------" + + ! 测试获取存在的组件 + info = component_registry%get("reconstructor", "weno3") + print *, "获取 weno3: " + call info%print() + + ! 测试获取不存在的组件 + info = component_registry%get("reconstructor", "non_existent") + print *, "获取 non_existent (应该为空): " + call info%print() + print *, "" + + ! 测试4: 清空功能 + print *, "测试4: 清空注册表" + print *, "-----------------" + call component_registry%clear() + print *, "清空后大小: ", component_registry%size() + call component_registry%list_all() + + print *, "" + print *, "=== 所有测试完成 ===" + +end program test_registry \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01a/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01a/CMakeLists.txt new file mode 100644 index 00000000..665f48ac --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01a/CMakeLists.txt @@ -0,0 +1,26 @@ +# 根目录CMakeLists.txt +#cmake_minimum_required(VERSION 3.12) +cmake_minimum_required(VERSION 4.2.1) +project(FortranRegistry VERSION 1.0.0 LANGUAGES Fortran) + +message(STATUS "CMAKE_Fortran_COMPILER=${CMAKE_Fortran_COMPILER}") +message(STATUS "CMAKE_Fortran_COMPILER_ID=${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "CMAKE_Fortran_COMPILER_VERSION=${CMAKE_Fortran_COMPILER_VERSION}") + +# 设置Fortran标准 +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +# 模块输出目录 +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) + +# 编译器选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "GNU") + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -Wall -Wextra") + set(CMAKE_Fortran_FLAGS_DEBUG "-g -O0 -fcheck=all -fbacktrace") + set(CMAKE_Fortran_FLAGS_RELEASE "-O3") +endif() + +# 添加子目录 +add_subdirectory(src) +add_subdirectory(tests) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01a/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01a/src/CMakeLists.txt new file mode 100644 index 00000000..0ddd08d2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01a/src/CMakeLists.txt @@ -0,0 +1,19 @@ +# src/CMakeLists.txt +# 源代码目录的CMake配置 + +message(STATUS "配置源代码目录...") + +# 添加核心模块子目录 +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/core AND + EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/core/CMakeLists.txt) + add_subdirectory(core) +else() + message(WARNING "core目录或CMakeLists.txt不存在") +endif() + +# 可选:添加其他模块子目录 +# if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/infrastructure) +# add_subdirectory(infrastructure) +# endif() + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01a/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01a/src/core/CMakeLists.txt new file mode 100644 index 00000000..9685b6fb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01a/src/core/CMakeLists.txt @@ -0,0 +1,16 @@ +# src/core/CMakeLists.txt +# 创建核心库 +add_library(core + registry.f90 +) + +# 设置模块输出目录 +target_include_directories(core PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 安装规则(可选) +install(TARGETS core + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01a/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/01a/src/core/registry.f90 new file mode 100644 index 00000000..1ffc879f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01a/src/core/registry.f90 @@ -0,0 +1,241 @@ +! src/core/registry.f90 +module registry_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + + ! 公开接口 + public :: wp, component_info, component_registry_type, component_registry + public :: register_component, initialize_registry, cleanup_registry + + ! 类型定义 + type :: component_info + character(len=50) :: category = "" + character(len=50) :: name = "" + integer :: order = 0 + contains + procedure :: print => ci_print + end type component_info + + type :: component_registry_type + private + type(component_info), allocatable :: components(:) + integer :: count = 0 + integer :: capacity = 10 + logical :: verbose = .true. + logical :: initialized = .false. + contains + procedure :: register => cr_register + procedure :: get => cr_get + procedure :: list_all => cr_list_all + procedure :: size => cr_size + procedure :: clear => cr_clear + end type component_registry_type + + ! 全局实例 + type(component_registry_type) :: component_registry + + ! 接口 - 确保两个版本都公开 + interface register_component + module procedure register_component_simple + module procedure register_component_with_order + end interface register_component + +contains + + ! ==================== 公共接口实现 ==================== + + ! 初始化注册表 + subroutine initialize_registry(initial_capacity, verbose) + integer, optional, intent(in) :: initial_capacity + logical, optional, intent(in) :: verbose + + if (component_registry%initialized) then + print *, "[WARN] Registry already initialized" + return + end if + + if (present(initial_capacity)) then + component_registry%capacity = max(10, initial_capacity) + end if + + if (present(verbose)) then + component_registry%verbose = verbose + end if + + component_registry%initialized = .true. + print *, "[INIT] Registry initialized, capacity:", component_registry%capacity + end subroutine initialize_registry + + ! 清理注册表 + subroutine cleanup_registry + call component_registry%clear() + component_registry%initialized = .false. + print *, "[CLEANUP] Registry cleaned up" + end subroutine cleanup_registry + + ! 简单注册(无阶数) + subroutine register_component_simple(category, name) + character(len=*), intent(in) :: category, name + type(component_info) :: info + + info%category = trim(category) + info%name = trim(name) + info%order = 0 + + call component_registry%register(category, name, info) + end subroutine register_component_simple + + ! 带阶数的注册 + subroutine register_component_with_order(category, name, order) + character(len=*), intent(in) :: category, name + integer, intent(in) :: order + type(component_info) :: info + + info%category = trim(category) + info%name = trim(name) + info%order = order + + call component_registry%register(category, name, info) + end subroutine register_component_with_order + + ! ==================== 内部方法 ==================== + + subroutine ci_print(this) + class(component_info), intent(in) :: this + if (this%order > 0) then + print *, " [", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")]" + else + print *, " [", trim(this%category), ".", trim(this%name), "]" + end if + end subroutine ci_print + + ! 内部注册实现 + subroutine cr_register(this, category, name, info) + class(component_registry_type), intent(inout) :: this + character(len=*), intent(in) :: category, name + type(component_info), intent(in) :: info + + type(component_info), allocatable :: temp(:) + integer :: i + + if (.not. this%initialized) then + print *, "[ERROR] Registry not initialized, call initialize_registry first" + return + end if + + ! 检查是否已存在 + do i = 1, this%count + if (trim(this%components(i)%category) == trim(category) .and. & + trim(this%components(i)%name) == trim(name)) then + if (this%verbose) then + print *, "[WARN] Overwriting: ", trim(category), ".", trim(name) + end if + this%components(i) = info + return + end if + end do + + ! 初始化数组 + if (.not. allocated(this%components)) then + allocate(this%components(this%capacity)) + end if + + ! 扩展数组 + if (this%count >= this%capacity) then + this%capacity = this%capacity * 2 + allocate(temp(this%capacity)) + temp(1:this%count) = this%components(1:this%count) + call move_alloc(temp, this%components) + if (this%verbose) then + print *, "[INFO] Registry expanded to:", this%capacity + end if + end if + + ! 添加组件 + this%count = this%count + 1 + this%components(this%count) = info + + if (this%verbose) then + print *, "[OK] Registered: ", trim(category), ".", trim(name) + end if + end subroutine cr_register + + function cr_get(this, category, name) result(info) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + type(component_info) :: info + + integer :: i + + info%category = "" + info%name = "" + info%order = 0 + + if (.not. this%initialized) then + if (this%verbose) then + print *, "[ERROR] Registry not initialized" + end if + return + end if + + do i = 1, this%count + if (trim(this%components(i)%category) == trim(category) .and. & + trim(this%components(i)%name) == trim(name)) then + info = this%components(i) + return + end if + end do + + if (this%verbose) then + print *, "[ERROR] Not found: ", trim(category), ".", trim(name) + end if + end function cr_get + + subroutine cr_list_all(this) + class(component_registry_type), intent(in) :: this + integer :: i + + if (.not. this%initialized) then + print *, "[ERROR] Registry not initialized" + return + end if + + print *, "=== Registry Contents (", this%count, " components) ===" + if (this%count == 0) then + print *, " (empty)" + return + end if + + do i = 1, this%count + call this%components(i)%print() + end do + end subroutine cr_list_all + + integer function cr_size(this) + class(component_registry_type), intent(in) :: this + cr_size = this%count + end function cr_size + + subroutine cr_clear(this) + class(component_registry_type), intent(inout) :: this + if (allocated(this%components)) then + deallocate(this%components) + end if + this%count = 0 + this%capacity = 10 + this%initialized = .false. + print *, "[CLEAR] Registry cleared" + end subroutine cr_clear + + ! 工具函数 + function str(i) result(s) + integer, intent(in) :: i + character(len=20) :: s + write(s, '(I0)') i + s = trim(adjustl(s)) + end function str + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01a/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01a/tests/CMakeLists.txt new file mode 100644 index 00000000..141f8045 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01a/tests/CMakeLists.txt @@ -0,0 +1,15 @@ +# tests/CMakeLists.txt +# 创建测试程序 +add_executable(test_registry + test_registry.f90 +) + +# 链接核心库 +target_link_libraries(test_registry + core +) + +# 设置模块路径 +target_include_directories(test_registry PRIVATE + ${CMAKE_Fortran_MODULE_DIRECTORY} +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01a/tests/test_registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/01a/tests/test_registry.f90 new file mode 100644 index 00000000..f6ad1777 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01a/tests/test_registry.f90 @@ -0,0 +1,75 @@ +! tests/test_registry.f90 +program test_registry + use registry_module + implicit none + + type(component_info) :: info + + print *, "=== Fortran Registry System Test ===" + print *, "" + + ! 初始化注册表 + call initialize_registry(verbose=.true.) + print *, "" + + ! 测试1: 基本注册 + print *, "Test 1: Basic Registration" + print *, "--------------------------" + + call component_registry%list_all() + print *, "Initial size: ", component_registry%size() + + ! 使用两种方式注册组件 + call register_component("reconstructor", "eno") + call register_component("reconstructor", "weno3") ! 简单版本 + call register_component("reconstructor", "weno5") + call register_component("flux", "rusanov") + call register_component("flux", "engquist-osher") + + print *, "After registration size: ", component_registry%size() + call component_registry%list_all() + print *, "" + + ! 测试2: 重复注册 + print *, "Test 2: Duplicate Registration" + print *, "-------------------------------" + call register_component("reconstructor", "eno") + print *, "" + + ! 测试3: 获取组件 + print *, "Test 3: Component Retrieval" + print *, "---------------------------" + + info = component_registry%get("reconstructor", "eno") + print *, "Get eno: " + call info%print() + + info = component_registry%get("reconstructor", "non_existent") + print *, "Get non_existent (should be empty): " + call info%print() + print *, "" + + ! 测试4: 清空注册表 + print *, "Test 4: Clear Registry" + print *, "----------------------" + call component_registry%clear() + print *, "Size after clear: ", component_registry%size() + call component_registry%list_all() + print *, "" + + ! 测试5: 重新初始化并注册 + print *, "Test 5: Re-initialize and register" + print *, "----------------------------------" + call initialize_registry(verbose=.false.) + call register_component("integrator", "rk1") + call register_component("integrator", "rk2") + call register_component("integrator", "rk3") + call component_registry%list_all() + print *, "" + + ! 清理 + call cleanup_registry() + + print *, "=== All Tests Completed Successfully ===" + +end program test_registry \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01b/CMakeLists.txt new file mode 100644 index 00000000..a2fe5bb7 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/CMakeLists.txt @@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + +# Set Fortran standard +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +# Module output directory +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) + +# Compiler flags for Intel Fortran +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + # Intel oneAPI/ifx compiler + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS}") + set(CMAKE_Fortran_FLAGS_DEBUG "/debug:full /Od /traceback /check:all /warn:all /fpe:0") + set(CMAKE_Fortran_FLAGS_RELEASE "/O3") + set(CMAKE_Fortran_FLAGS_RELWITHDEBINFO "/O2 /debug:full") +elseif(CMAKE_Fortran_COMPILER_ID MATCHES "GNU") + # GNU gfortran compiler + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -Wall -Wextra") + set(CMAKE_Fortran_FLAGS_DEBUG "-g -O0 -fcheck=all -fbacktrace -ffpe-trap=invalid,zero,overflow") + set(CMAKE_Fortran_FLAGS_RELEASE "-O3 -march=native") + set(CMAKE_Fortran_FLAGS_RELWITHDEBINFO "-O2 -g") +endif() + +# Set default build type +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Build type" FORCE) + message(STATUS "Setting build type to: ${CMAKE_BUILD_TYPE}") +endif() + +message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") + +# Create module directory +file(MAKE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY}) + +# Add subdirectories +add_subdirectory(src) +add_subdirectory(tests) + +# Install directory +set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/install CACHE PATH "Installation prefix") + +message(STATUS "Installation prefix: ${CMAKE_INSTALL_PREFIX}") +message(STATUS "Module directory: ${CMAKE_Fortran_MODULE_DIRECTORY}") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01b/src/CMakeLists.txt new file mode 100644 index 00000000..ee38952b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/src/CMakeLists.txt @@ -0,0 +1,14 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01b/src/core/CMakeLists.txt new file mode 100644 index 00000000..376dd4fe --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/src/core/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/core/CMakeLists.txt +message(STATUS "配置核心模块...") + +add_library(core + registry.f90 + factory_interfaces.f90 +) + +target_include_directories(core PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 安装规则 +install(TARGETS core + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) +install(FILES + ${CMAKE_Fortran_MODULE_DIRECTORY}/registry_module.mod + ${CMAKE_Fortran_MODULE_DIRECTORY}/factory_interfaces.mod + DESTINATION include/fortran_cfd/core +) + +message(STATUS "核心模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/01b/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..b410321d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/src/core/factory_interfaces.f90 @@ -0,0 +1,23 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp + + ! 抽象工厂接口 + type, abstract :: base_factory + contains + procedure(create_interface), deferred :: create + end type base_factory + + abstract interface + subroutine create_interface(this, instance) + import :: base_factory + class(base_factory), intent(in) :: this + class(*), allocatable, intent(out) :: instance + end subroutine create_interface + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/01b/src/core/registry.f90 new file mode 100644 index 00000000..68091c6b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/src/core/registry.f90 @@ -0,0 +1,423 @@ +! src/core/registry.f90 +module registry_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + + ! ==================== 公开接口 ==================== + public :: wp, component_info, component_registry + public :: register_component, create_component + public :: initialize_registry, cleanup_registry + public :: has_component, get_available_components + + ! ==================== 类型定义 ==================== + + ! 工厂过程接口 + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + + ! 组件信息类型 + type :: component_info + character(len=32) :: category = "" + character(len=32) :: name = "" + integer :: order = 0 + procedure(factory_procedure), pointer, nopass :: factory => null() + logical :: has_factory = .false. + contains + procedure :: print => ci_print + procedure :: create => ci_create + end type component_info + + ! 注册表类型 + type :: component_registry_type + private + type(component_info), allocatable :: components(:) + integer :: count = 0 + integer :: capacity = 100 + logical :: verbose = .true. + logical :: initialized = .false. + contains + procedure, private :: register_info => cr_register_info + procedure :: get => cr_get + procedure :: list_all => cr_list_all + procedure :: clear => cr_clear + procedure :: size => cr_size + procedure, private :: expand => cr_expand + end type component_registry_type + + ! 全局注册表实例 + type(component_registry_type), save :: component_registry + + ! 接口重载 + interface register_component + module procedure register_component_simple + module procedure register_component_with_factory + module procedure register_component_full + end interface register_component + +contains + + ! ==================== 公共API ==================== + + ! 初始化注册表 + subroutine initialize_registry(initial_capacity, verbose) + integer, optional, intent(in) :: initial_capacity + logical, optional, intent(in) :: verbose + + if (component_registry%initialized) then + if (component_registry%verbose) then + print *, "[INFO] 注册表已初始化" + end if + return + end if + + if (present(initial_capacity)) then + component_registry%capacity = max(10, initial_capacity) + end if + + if (present(verbose)) then + component_registry%verbose = verbose + end if + + ! 分配数组 + allocate(component_registry%components(component_registry%capacity)) + + component_registry%initialized = .true. + component_registry%count = 0 + + if (component_registry%verbose) then + print *, "[INIT] 注册表初始化完成,容量:", component_registry%capacity + end if + end subroutine initialize_registry + + ! 清理注册表 + subroutine cleanup_registry + call component_registry%clear() + if (component_registry%verbose) then + print *, "[CLEANUP] 注册表清理完成" + end if + end subroutine cleanup_registry + + ! 简单注册(只有名称) + subroutine register_component_simple(category, name) + character(len=*), intent(in) :: category, name + type(component_info) :: info + + info%category = to_lower(trim(adjustl(category))) + info%name = to_lower(trim(adjustl(name))) + info%order = 0 + info%factory => null() + info%has_factory = .false. + + call component_registry%register_info(info) + end subroutine register_component_simple + + ! 带阶数的注册 + subroutine register_component_with_factory(category, name, factory_proc, order) + character(len=*), intent(in) :: category, name + procedure(factory_procedure) :: factory_proc + integer, optional, intent(in) :: order + + type(component_info) :: info + + info%category = to_lower(trim(adjustl(category))) + info%name = to_lower(trim(adjustl(name))) + + if (present(order)) then + info%order = order + else + info%order = 0 + end if + + info%factory => factory_proc + info%has_factory = .true. + + call component_registry%register_info(info) + end subroutine register_component_with_factory + + ! 完整注册 + subroutine register_component_full(category, name, order, has_factory) + character(len=*), intent(in) :: category, name + integer, intent(in) :: order + logical, intent(in) :: has_factory + + type(component_info) :: info + + info%category = to_lower(trim(adjustl(category))) + info%name = to_lower(trim(adjustl(name))) + info%order = order + info%factory => null() + info%has_factory = has_factory + + call component_registry%register_info(info) + end subroutine register_component_full + + ! 创建组件实例 + subroutine create_component(category, name, instance) + character(len=*), intent(in) :: category, name + class(*), allocatable, intent(out) :: instance + + type(component_info) :: info + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + info = component_registry%get(cat_lower, name_lower) + + if (len_trim(info%category) == 0) then + error stop "[ERROR] 组件未找到: " // trim(cat_lower) // "." // trim(name_lower) + end if + + if (.not. info%has_factory) then + error stop "[ERROR] 组件没有工厂函数: " // trim(cat_lower) // "." // trim(name_lower) + end if + + if (.not. associated(info%factory)) then + error stop "[ERROR] 工厂函数未关联: " // trim(cat_lower) // "." // trim(name_lower) + end if + + call info%create(instance) + end subroutine create_component + + ! 检查组件是否存在 + function has_component(category, name) result(found) + character(len=*), intent(in) :: category, name + logical :: found + + type(component_info) :: info + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + info = component_registry%get(cat_lower, name_lower) + found = (len_trim(info%category) > 0) + end function has_component + + ! 获取某类别下的所有组件 + subroutine get_available_components(category, names, orders) + character(len=*), intent(in) :: category + character(len=:), allocatable, intent(out), optional :: names(:) + integer, allocatable, intent(out), optional :: orders(:) + + character(len=32) :: cat_lower + integer :: i, count, idx + type(component_info) :: info + + cat_lower = to_lower(trim(adjustl(category))) + + ! 先计算数量 + count = 0 + do i = 1, component_registry%count + if (component_registry%components(i)%category == cat_lower) then + count = count + 1 + end if + end do + + ! 分配数组 + if (present(names)) then + allocate(character(len=32) :: names(count)) + end if + + if (present(orders)) then + allocate(orders(count)) + end if + + ! 填充数组 + idx = 1 + do i = 1, component_registry%count + if (component_registry%components(i)%category == cat_lower) then + info = component_registry%components(i) + if (present(names)) then + names(idx) = info%name + end if + if (present(orders)) then + orders(idx) = info%order + end if + idx = idx + 1 + end if + end do + end subroutine get_available_components + + ! ==================== 组件信息方法 ==================== + + subroutine ci_print(this) + class(component_info), intent(in) :: this + + if (this%order > 0) then + if (this%has_factory) then + print *, " [", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ", has factory)]" + else + print *, " [", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")]" + end if + else + if (this%has_factory) then + print *, " [", trim(this%category), ".", trim(this%name), & + " (has factory)]" + else + print *, " [", trim(this%category), ".", trim(this%name), "]" + end if + end if + end subroutine ci_print + + subroutine ci_create(this, instance) + class(component_info), intent(in) :: this + class(*), allocatable, intent(out) :: instance + + if (.not. this%has_factory) then + error stop "[ERROR] 组件没有工厂函数" + end if + + if (.not. associated(this%factory)) then + error stop "[ERROR] 工厂函数未关联" + end if + + call this%factory(instance) + end subroutine ci_create + + ! ==================== 注册表内部方法 ==================== + + subroutine cr_register_info(this, info) + class(component_registry_type), intent(inout) :: this + type(component_info), intent(in) :: info + + integer :: i + + if (.not. this%initialized) then + error stop "[ERROR] 注册表未初始化,请先调用 initialize_registry" + end if + + ! 检查是否已存在 + do i = 1, this%count + if (this%components(i)%category == info%category .and. & + this%components(i)%name == info%name) then + if (this%verbose) then + print *, "[WARN] 覆盖注册: ", & + trim(info%category), ".", trim(info%name) + end if + this%components(i) = info + return + end if + end do + + ! 检查是否需要扩展 + if (this%count >= this%capacity) then + call this%expand() + end if + + ! 添加组件 + this%count = this%count + 1 + this%components(this%count) = info + + if (this%verbose) then + print *, "[OK] 已注册: ", trim(info%category), ".", trim(info%name) + end if + end subroutine cr_register_info + + function cr_get(this, category, name) result(info) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + type(component_info) :: info + + integer :: i + + ! 初始化返回值为空 + info%category = "" + info%name = "" + info%order = 0 + info%factory => null() + info%has_factory = .false. + + if (.not. this%initialized) then + return + end if + + do i = 1, this%count + if (this%components(i)%category == category .and. & + this%components(i)%name == name) then + info = this%components(i) + return + end if + end do + end function cr_get + + subroutine cr_list_all(this) + class(component_registry_type), intent(in) :: this + integer :: i + + if (.not. this%initialized) then + print *, "[INFO] 注册表未初始化" + return + end if + + print *, "=== 注册表内容 (", this%count, " 个组件) ===" + + if (this%count == 0) then + print *, " (空)" + return + end if + + ! 按类别分组显示 + do i = 1, this%count + call this%components(i)%print() + end do + + print *, "======================================" + end subroutine cr_list_all + + subroutine cr_clear(this) + class(component_registry_type), intent(inout) :: this + + if (allocated(this%components)) then + deallocate(this%components) + end if + + this%count = 0 + this%capacity = 100 + this%initialized = .false. + end subroutine cr_clear + + integer function cr_size(this) + class(component_registry_type), intent(in) :: this + cr_size = this%count + end function cr_size + + subroutine cr_expand(this) + class(component_registry_type), intent(inout) :: this + type(component_info), allocatable :: temp(:) + + this%capacity = this%capacity * 2 + allocate(temp(this%capacity)) + temp(1:this%count) = this%components(1:this%count) + call move_alloc(temp, this%components) + + if (this%verbose) then + print *, "[INFO] 注册表扩展至容量:", this%capacity + end if + end subroutine cr_expand + + ! ==================== 工具函数 ==================== + + function to_lower(str) result(lower_str) + character(len=*), intent(in) :: str + character(len=len(str)) :: lower_str + integer :: i + + do i = 1, len(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + lower_str(i:i) = char(ichar(str(i:i)) + 32) + else + lower_str(i:i) = str(i:i) + end if + end do + end function to_lower + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/src/core/registry_advanced.f90 b/example/1d-linear-convection/weno3/fortran/registry/01b/src/core/registry_advanced.f90 new file mode 100644 index 00000000..4b5aa05a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/src/core/registry_advanced.f90 @@ -0,0 +1,215 @@ +! src/core/registry_advanced.f90 +module registry_advanced_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use factory_interfaces, only: factory_procedure + implicit none + + private + public :: wp, component_info, component_registry, register_factory, create_instance + + ! 组件信息类型(增强版) + type :: component_info + character(len=50) :: category = "" + character(len=50) :: name = "" + integer :: order = 0 + procedure(factory_procedure), pointer, nopass :: factory => null() + logical :: has_factory = .false. + contains + procedure :: print => ci_print + procedure :: create => ci_create + end type component_info + + ! 注册表类型 + type :: component_registry_type + private + type(component_info), allocatable :: components(:) + integer :: count = 0 + integer :: capacity = 10 + logical :: verbose = .true. + contains + procedure :: register => cr_register + procedure :: get => cr_get + procedure :: list_all => cr_list_all + procedure :: create => cr_create + end type component_registry_type + + type(component_registry_type) :: component_registry + + ! 接口 + interface register_factory + module procedure register_factory_simple + module procedure register_factory_with_order + end interface register_factory + +contains + + ! ==================== 组件信息方法 ==================== + + subroutine ci_print(this) + class(component_info), intent(in) :: this + character(len=100) :: factory_str + + if (this%has_factory) then + factory_str = " [has factory]" + else + factory_str = "" + end if + + if (this%order > 0) then + print *, " [", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")", trim(factory_str), "]" + else + print *, " [", trim(this%category), ".", trim(this%name), & + trim(factory_str), "]" + end if + end subroutine ci_print + + subroutine ci_create(this, instance) + class(component_info), intent(in) :: this + class(*), allocatable, intent(out) :: instance + + if (.not. this%has_factory) then + error stop "[ERROR] Component has no factory" + end if + + call this%factory(instance) + end subroutine ci_create + + ! ==================== 注册表方法 ==================== + + ! 注册工厂(简单版) + subroutine register_factory_simple(category, name, factory_proc) + character(len=*), intent(in) :: category, name + procedure(factory_procedure) :: factory_proc + + type(component_info) :: info + + info%category = trim(category) + info%name = trim(name) + info%order = 0 + info%factory => factory_proc + info%has_factory = .true. + + call component_registry%register(info) + end subroutine register_factory_simple + + ! 注册工厂(带阶数) + subroutine register_factory_with_order(category, name, factory_proc, order) + character(len=*), intent(in) :: category, name + procedure(factory_procedure) :: factory_proc + integer, intent(in) :: order + + type(component_info) :: info + + info%category = trim(category) + info%name = trim(name) + info%order = order + info%factory => factory_proc + info%has_factory = .true. + + call component_registry%register(info) + end subroutine register_factory_with_order + + ! 内部注册实现 + subroutine cr_register(this, info) + class(component_registry_type), intent(inout) :: this + type(component_info), intent(in) :: info + + type(component_info), allocatable :: temp(:) + integer :: i + + ! 检查是否已存在 + do i = 1, this%count + if (trim(this%components(i)%category) == trim(info%category) .and. & + trim(this%components(i)%name) == trim(info%name)) then + if (this%verbose) then + print *, "[WARN] Overwriting: ", & + trim(info%category), ".", trim(info%name) + end if + this%components(i) = info + return + end if + end do + + ! 初始化数组 + if (.not. allocated(this%components)) then + allocate(this%components(this%capacity)) + end if + + ! 扩展数组 + if (this%count >= this%capacity) then + this%capacity = this%capacity * 2 + allocate(temp(this%capacity)) + temp(1:this%count) = this%components(1:this%count) + call move_alloc(temp, this%components) + end if + + ! 添加组件 + this%count = this%count + 1 + this%components(this%count) = info + + if (this%verbose) then + print *, "[OK] Registered: ", trim(info%category), ".", trim(info%name) + end if + end subroutine cr_register + + function cr_get(this, category, name) result(info) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + type(component_info) :: info + + integer :: i + + ! 初始化为空 + info%category = "" + info%name = "" + info%order = 0 + info%factory => null() + info%has_factory = .false. + + do i = 1, this%count + if (trim(this%components(i)%category) == trim(category) .and. & + trim(this%components(i)%name) == trim(name)) then + info = this%components(i) + return + end if + end do + + if (this%verbose) then + print *, "[ERROR] Not found: ", trim(category), ".", trim(name) + end if + end function cr_get + + ! 创建实例 + subroutine cr_create(this, category, name, instance) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + class(*), allocatable, intent(out) :: instance + + type(component_info) :: info + + info = this%get(category, name) + + if (len_trim(info%category) > 0 .and. info%has_factory) then + call info%create(instance) + else + error stop "[ERROR] Cannot create instance: factory not available" + end if + end subroutine cr_create + + subroutine cr_list_all(this) + class(component_registry_type), intent(in) :: this + integer :: i + + print *, "=== Registry Contents (", this%count, " components) ===" + if (this%count == 0) then + print *, " (empty)" + return + end if + + do i = 1, this%count + call this%components(i)%print() + end do + end subroutine cr_list_all + +end module registry_advanced_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01b/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..2b72c548 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/src/infrastructure/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "配置基础设施模块...") + +add_library(infrastructure + config.f90 + mesh.f90 +) + +target_link_libraries(infrastructure PUBLIC core) + +target_include_directories(infrastructure PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS infrastructure + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "基础设施模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/01b/src/infrastructure/config.f90 new file mode 100644 index 00000000..e8e003ae --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/src/infrastructure/config.f90 @@ -0,0 +1,117 @@ +! src/infrastructure/config.f90 +module config_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use registry_module, only: register_component + implicit none + + private + public :: wp, cfd_config + + ! CFD配置类型 + type :: cfd_config + character(len=20) :: ic_type = "step" + character(len=20) :: recon_scheme = "eno" + character(len=20) :: flux_type = "rusanov" + integer :: rk_order = 1 + real(wp) :: wave_speed = 1.0_wp + real(wp) :: final_time = 0.625_wp + real(wp) :: dt = 0.025_wp + character(len=20) :: boundary_type = "periodic" + real(wp) :: left_boundary_value = 1.0_wp + real(wp) :: right_boundary_value = 2.0_wp + integer :: spatial_order = 2 + logical :: verbose = .true. + contains + procedure :: with_reconstruction => config_with_reconstruction + procedure :: with_boundary => config_with_boundary + procedure :: print => config_print + end type cfd_config + +contains + + subroutine config_with_reconstruction(this, scheme, order) + class(cfd_config), intent(inout) :: this + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + character(len=20) :: scheme_lower + + ! 转换为小写 + scheme_lower = scheme + call to_lower_inplace(scheme_lower) + this%recon_scheme = trim(adjustl(scheme_lower)) + + ! 设置阶数 + if (present(order)) then + this%spatial_order = order + else + ! 智能默认 + if (index(this%recon_scheme, 'weno') > 0) then + this%spatial_order = 5 + else if (trim(this%recon_scheme) == 'eno') then + this%spatial_order = 3 + else + error stop "[ERROR] 不支持的重建格式: " // trim(this%recon_scheme) + end if + end if + + if (this%verbose) then + print *, "[CONFIG] 重建方案: ", trim(this%recon_scheme), & + " 阶数: ", this%spatial_order + end if + end subroutine config_with_reconstruction + + subroutine config_with_boundary(this, bc_type, left_value, right_value) + class(cfd_config), intent(inout) :: this + character(len=*), intent(in) :: bc_type + real(wp), optional, intent(in) :: left_value + real(wp), optional, intent(in) :: right_value + + this%boundary_type = trim(adjustl(bc_type)) + + if (present(left_value)) then + this%left_boundary_value = left_value + end if + + if (present(right_value)) then + this%right_boundary_value = right_value + end if + + if (this%verbose) then + print *, "[CONFIG] 边界条件: ", trim(this%boundary_type), & + " 左值: ", this%left_boundary_value, & + " 右值: ", this%right_boundary_value + end if + end subroutine config_with_boundary + + subroutine config_print(this) + class(cfd_config), intent(in) :: this + + print *, "=== CFD 配置 ===" + print *, "初始条件: ", trim(this%ic_type) + print *, "重建方案: ", trim(this%recon_scheme), " (order:", this%spatial_order, ")" + print *, "通量类型: ", trim(this%flux_type) + print *, "时间积分: RK", this%rk_order + print *, "波速: ", this%wave_speed + print *, "最终时间: ", this%final_time + print *, "时间步长: ", this%dt + print *, "边界条件: ", trim(this%boundary_type) + if (trim(this%boundary_type) == 'dirichlet') then + print *, " Dirichlet值: [", this%left_boundary_value, ", ", & + this%right_boundary_value, "]" + end if + print *, "==============================" + end subroutine config_print + + subroutine to_lower_inplace(str) + character(len=*), intent(inout) :: str + integer :: i + + do i = 1, len_trim(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + str(i:i) = char(ichar(str(i:i)) + 32) + end if + end do + end subroutine to_lower_inplace + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/01b/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..65a45dcd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/src/infrastructure/mesh.f90 @@ -0,0 +1,74 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, mesh_type, mesh_init, mesh_print_info + + ! 网格类型 + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer :: ncells = 40 + integer :: nnodes + integer :: nx + real(wp), allocatable :: x(:) ! 节点坐标 + real(wp), allocatable :: xcc(:) ! 单元中心坐标 + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer, optional, intent(in) :: ncells + + integer :: i + + ! 设置参数 + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! 计算派生参数 + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! 分配数组 + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! 生成节点坐标 + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! 生成单元中心坐标 + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== 网格信息 ===" + print *, "计算域: [", this%xmin, ", ", this%xmax, "]" + print *, "单元数: ", this%ncells + print *, "节点数: ", this%nnodes + print *, "网格尺寸 dx: ", this%dx + print *, "域长度 L: ", this%L + print *, "==========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..e024ec4b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,21 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +add_library(flux + base.f90 + rusanov.f90 + factory.f90 +) + +target_link_libraries(flux PUBLIC core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/flux/base.f90 new file mode 100644 index 00000000..57fc04da --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/flux/base.f90 @@ -0,0 +1,35 @@ +! src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, flux_calculator_base + + ! 通量计算器抽象基类 + type, abstract :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure(compute_interface), deferred :: compute + procedure :: info => flux_info + end type flux_calculator_base + + abstract interface + subroutine compute_interface(this, qL, qR, flux, wave_speed) + import :: flux_calculator_base, wp + class(flux_calculator_base), intent(in) :: this + real(wp), intent(in) :: qL(:), qR(:) + real(wp), intent(out) :: flux(:) + real(wp), intent(in) :: wave_speed + end subroutine compute_interface + end interface + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "通量计算器信息:" + print *, " 名称: ", trim(this%name) + end subroutine flux_info + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/flux/factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/flux/factory.f90 new file mode 100644 index 00000000..3a4bb3f8 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/flux/factory.f90 @@ -0,0 +1,47 @@ +! src/numerics/flux/factory.f90 +module flux_factory_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use registry_module, only: create_component, has_component + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: wp, flux_factory_create + +contains + + subroutine flux_factory_create(flux_name, flux_calculator) + character(len=*), intent(in) :: flux_name + class(flux_calculator_base), allocatable, intent(out) :: flux_calculator + + class(*), allocatable :: instance + character(len=20) :: name_lower + integer :: i + + ! Convert to lowercase + name_lower = flux_name + do i = 1, len_trim(name_lower) + if (name_lower(i:i) >= 'A' .and. name_lower(i:i) <= 'Z') then + name_lower(i:i) = char(ichar(name_lower(i:i)) + 32) + end if + end do + + ! Check if registered + if (.not. has_component("flux", trim(name_lower))) then + print *, "[ERROR] Flux calculator not registered: ", trim(flux_name) + return + end if + + ! Create instance + call create_component("flux", trim(name_lower), instance) + + ! Type conversion + select type (inst => instance) + type is (flux_calculator_base) + allocate(flux_calculator, source=inst) + class default + error stop "[ERROR] Created flux calculator has wrong type" + end select + end subroutine flux_factory_create + +end module flux_factory_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..4f96137f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/flux/rusanov.f90 @@ -0,0 +1,70 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use flux_base_module, only: flux_calculator_base, wp + use registry_module, only: register_component_with_factory + implicit none + + private + public :: wp, rusanov_flux, create_rusanov + + type, extends(flux_calculator_base) :: rusanov_flux + real(wp) :: wave_speed_default = 1.0_wp + contains + procedure :: compute => rusanov_compute + procedure :: info => rusanov_info + end type rusanov_flux + +contains + + ! Factory function + subroutine create_rusanov(instance) + class(*), allocatable, intent(out) :: instance + type(rusanov_flux), allocatable :: flux + + allocate(flux) + flux%name = "Rusanov" + flux%wave_speed_default = 1.0_wp + + call move_alloc(flux, instance) + end subroutine create_rusanov + + ! Rusanov flux computation + subroutine rusanov_compute(this, qL, qR, flux, wave_speed) + class(rusanov_flux), intent(in) :: this + real(wp), intent(in) :: qL(:), qR(:) + real(wp), intent(out) :: flux(:) + real(wp), intent(in) :: wave_speed + + integer :: i, n + real(wp) :: max_speed, fL, fR + + n = size(qL) + + ! Ensure arrays have same size + if (size(qR) /= n .or. size(flux) /= n) then + error stop "[ERROR] Array size mismatch in rusanov_compute" + end if + + ! Compute maximum wave speed + max_speed = abs(wave_speed) + + ! Compute Rusanov flux at each interface + do i = 1, n + ! Simple linear convection flux: f(u) = wave_speed * u + fL = wave_speed * qL(i) + fR = wave_speed * qR(i) + + ! Rusanov flux formula + flux(i) = 0.5_wp * (fL + fR) - 0.5_wp * max_speed * (qR(i) - qL(i)) + end do + end subroutine rusanov_compute + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + call flux_info(this) + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..68bf7c35 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,23 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "配置重构器模块...") + +add_library(reconstructor + base.f90 + eno.f90 + weno3.f90 + weno5.f90 + factory.f90 +) + +target_link_libraries(reconstructor PUBLIC core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "重构器模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..bceda679 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/reconstructor/base.f90 @@ -0,0 +1,40 @@ +! src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, reconstructor_base + + ! 重构器抽象基类 + type, abstract :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + real(wp) :: epsilon = 1.0e-6_wp + contains + procedure(reconstruct_interface), deferred :: reconstruct + procedure :: info => reconstructor_info + end type reconstructor_base + + abstract interface + subroutine reconstruct_interface(this, q, qL, qR) + import :: reconstructor_base, wp + class(reconstructor_base), intent(in) :: this + real(wp), intent(in) :: q(:) + real(wp), intent(out) :: qL(:) + real(wp), intent(out) :: qR(:) + end subroutine reconstruct_interface + end interface + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + + print *, "重构器信息:" + print *, " 名称: ", trim(this%name) + print *, " 阶数: ", this%order + print *, " epsilon: ", this%epsilon + end subroutine reconstructor_info + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..737b6bf0 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,111 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use reconstructor_base_module, only: reconstructor_base, wp + use registry_module, only: register_component_with_factory + implicit none + + private + public :: wp, eno_reconstructor, create_eno + + type, extends(reconstructor_base) :: eno_reconstructor + real(wp), allocatable :: coef(:,:) + real(wp), allocatable :: dd(:,:) + integer, allocatable :: lmc(:) + contains + procedure :: reconstruct => eno_reconstruct + procedure :: info => eno_info + procedure :: initialize => eno_initialize + end type eno_reconstructor + +contains + + ! 工厂函数 + subroutine create_eno(instance) + class(*), allocatable, intent(out) :: instance + type(eno_reconstructor), allocatable :: eno + + allocate(eno) + eno%name = "ENO" + eno%order = 3 + eno%epsilon = 1.0e-6_wp + + call move_alloc(eno, instance) + end subroutine create_eno + + ! 初始化ENO系数 + subroutine eno_initialize(this, order) + class(eno_reconstructor), intent(inout) :: this + integer, intent(in) :: order + + integer :: i + + this%order = order + + ! 分配系数数组 + if (allocated(this%coef)) deallocate(this%coef) + if (allocated(this%dd)) deallocate(this%dd) + if (allocated(this%lmc)) deallocate(this%lmc) + + allocate(this%coef(order+1, order)) + allocate(this%dd(order, 1000)) ! 临时大小 + allocate(this%lmc(1000)) ! 临时大小 + + ! 初始化ENO系数(简化版本,后续需要完善) + this%coef = 0.0_wp + + ! 这里应该填充ENO系数表 + ! 为了简化,我们先使用线性插值系数 + if (order == 1) then + this%coef(1,1) = 1.0_wp + this%coef(2,1) = 1.0_wp + elseif (order == 2) then + this%coef(1,1:2) = [1.5_wp, -0.5_wp] + this%coef(2,1:2) = [0.5_wp, 0.5_wp] + this%coef(3,1:2) = [-0.5_wp, 1.5_wp] + elseif (order == 3) then + this%coef(1,1:3) = [11.0_wp/6.0_wp, -7.0_wp/6.0_wp, 1.0_wp/3.0_wp] + this%coef(2,1:3) = [1.0_wp/3.0_wp, 5.0_wp/6.0_wp, -1.0_wp/6.0_wp] + this%coef(3,1:3) = [-1.0_wp/6.0_wp, 5.0_wp/6.0_wp, 1.0_wp/3.0_wp] + this%coef(4,1:3) = [1.0_wp/3.0_wp, -7.0_wp/6.0_wp, 11.0_wp/6.0_wp] + end if + end subroutine eno_initialize + + ! ENO重构实现(简化版本) + subroutine eno_reconstruct(this, q, qL, qR) + class(eno_reconstructor), intent(in) :: this + real(wp), intent(in) :: q(:) + real(wp), intent(out) :: qL(:), qR(:) + + integer :: i, n + + n = size(q) - 1 + + if (this%order == 1) then + ! 1阶ENO:简单上风 + do i = 1, n + qL(i) = q(i) + qR(i) = q(i+1) + end do + elseif (this%order == 2) then + ! 2阶ENO:线性插值 + do i = 1, n + qL(i) = 1.5_wp*q(i) - 0.5_wp*q(i-1) + qR(i) = -0.5_wp*q(i) + 1.5_wp*q(i+1) + end do + else + ! 默认:简单上风 + do i = 1, n + qL(i) = q(i) + qR(i) = q(i+1) + end do + end if + end subroutine eno_reconstruct + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + call reconstructor_info(this) + print *, " 类型: ENO重构器" + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/reconstructor/eno_type.f90 b/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/reconstructor/eno_type.f90 new file mode 100644 index 00000000..0f12edcb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/reconstructor/eno_type.f90 @@ -0,0 +1,59 @@ +! src/numerics/reconstructor/eno_type.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, eno_reconstructor, create_eno + + type :: eno_reconstructor + integer :: order = 3 + real(wp) :: epsilon = 1.0e-6_wp + character(len=50) :: name = "ENO" + contains + procedure :: compute => eno_compute + procedure :: info => eno_info + end type eno_reconstructor + +contains + + ! 工厂函数 + subroutine create_eno(instance) + class(*), allocatable, intent(out) :: instance + type(eno_reconstructor), allocatable :: eno + + allocate(eno) + eno%name = "ENO Reconstructor" + eno%order = 3 + eno%epsilon = 1.0e-6_wp + + call move_alloc(eno, instance) + end subroutine create_eno + + ! 计算接口 + subroutine eno_compute(this, q, qL, qR) + class(eno_reconstructor), intent(in) :: this + real(wp), intent(in) :: q(:) + real(wp), intent(out) :: qL(:), qR(:) + integer :: i, n + + n = size(q) - 1 + print *, "[ENO] Computing interface values for ", n, " cells" + + ! 简化实现:复制中间值 + do i = 1, n + qL(i) = q(i) + qR(i) = q(i+1) + end do + end subroutine eno_compute + + ! 信息输出 + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + print *, "ENO Reconstructor:" + print *, " Order: ", this%order + print *, " Epsilon: ", this%epsilon + print *, " Name: ", trim(this%name) + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/reconstructor/factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/reconstructor/factory.f90 new file mode 100644 index 00000000..63d3d8b8 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/reconstructor/factory.f90 @@ -0,0 +1,47 @@ +! src/numerics/reconstructor/factory.f90 +module reconstructor_factory_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use registry_module, only: create_component, has_component + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: wp, reconstructor_factory_create + +contains + + subroutine reconstructor_factory_create(scheme_name, reconstructor) + character(len=*), intent(in) :: scheme_name + class(reconstructor_base), allocatable, intent(out) :: reconstructor + + class(*), allocatable :: instance + character(len=20) :: name_lower + integer :: i + + ! 转换为小写 + name_lower = scheme_name + do i = 1, len_trim(name_lower) + if (name_lower(i:i) >= 'A' .and. name_lower(i:i) <= 'Z') then + name_lower(i:i) = char(ichar(name_lower(i:i)) + 32) + end if + end do + + ! 检查是否已注册 + if (.not. has_component("reconstructor", trim(name_lower))) then + print *, "[ERROR] 重构器未注册: ", trim(scheme_name) + return + end if + + ! 创建实例 + call create_component("reconstructor", trim(name_lower), instance) + + ! 类型转换 + select type (inst => instance) + type is (reconstructor_base) + allocate(reconstructor, source=inst) + class default + error stop "[ERROR] 创建的重构器类型错误" + end select + end subroutine reconstructor_factory_create + +end module reconstructor_factory_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..d49ad949 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,88 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use reconstructor_base_module, only: reconstructor_base, wp + use registry_module, only: register_component_with_factory + implicit none + + private + public :: wp, weno3_reconstructor, create_weno3 + + type, extends(reconstructor_base) :: weno3_reconstructor + contains + procedure :: reconstruct => weno3_reconstruct + procedure :: info => weno3_info + end type weno3_reconstructor + +contains + + ! 工厂函数 + subroutine create_weno3(instance) + class(*), allocatable, intent(out) :: instance + type(weno3_reconstructor), allocatable :: weno3 + + allocate(weno3) + weno3%name = "WENO3" + weno3%order = 3 + weno3%epsilon = 1.0e-6_wp + + call move_alloc(weno3, instance) + end subroutine create_weno3 + + ! WENO-3重构实现(简化版本) + subroutine weno3_reconstruct(this, q, qL, qR) + class(weno3_reconstructor), intent(in) :: this + real(wp), intent(in) :: q(:) + real(wp), intent(out) :: qL(:), qR(:) + + integer :: i, n + real(wp) :: beta0, beta1, alpha0, alpha1, alpha, w0, w1 + real(wp) :: q0, q1, v0, v1, v2 + + n = size(q) - 2 ! 需要2个ghost cells + + do i = 2, n+1 + ! 获取模板值 + v0 = q(i-1) + v1 = q(i) + v2 = q(i+1) + + ! 计算左界面值 qL(i-1) + beta0 = (v1 - v0)**2 + beta1 = (v2 - v1)**2 + + alpha0 = 1.0_wp/3.0_wp / (this%epsilon + beta0)**2 + alpha1 = 2.0_wp/3.0_wp / (this%epsilon + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + + q0 = -0.5_wp*v0 + 1.5_wp*v1 + q1 = 0.5_wp*v1 + 0.5_wp*v2 + + qL(i-1) = w0 * q0 + w1 * q1 + + ! 计算右界面值 qR(i-1) + beta0 = (v1 - v0)**2 + beta1 = (v2 - v1)**2 + + alpha0 = 2.0_wp/3.0_wp / (this%epsilon + beta0)**2 + alpha1 = 1.0_wp/3.0_wp / (this%epsilon + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + + q0 = 0.5_wp*v0 + 0.5_wp*v1 + q1 = 1.5_wp*v1 - 0.5_wp*v2 + + qR(i-1) = w0 * q0 + w1 * q1 + end do + end subroutine weno3_reconstruct + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + call reconstructor_info(this) + print *, " 类型: WENO-3重构器" + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/reconstructor/weno5.f90 b/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/reconstructor/weno5.f90 new file mode 100644 index 00000000..19efbb89 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/src/numerics/reconstructor/weno5.f90 @@ -0,0 +1,106 @@ +! src/numerics/reconstructor/weno5.f90 +module weno5_reconstructor_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use reconstructor_base_module, only: reconstructor_base, wp + use registry_module, only: register_component_with_factory + implicit none + + private + public :: wp, weno5_reconstructor, create_weno5 + + type, extends(reconstructor_base) :: weno5_reconstructor + contains + procedure :: reconstruct => weno5_reconstruct + procedure :: info => weno5_info + end type weno5_reconstructor + +contains + + ! 工厂函数 + subroutine create_weno5(instance) + class(*), allocatable, intent(out) :: instance + type(weno5_reconstructor), allocatable :: weno5 + + allocate(weno5) + weno5%name = "WENO5" + weno5%order = 5 + weno5%epsilon = 1.0e-6_wp + + call move_alloc(weno5, instance) + end subroutine create_weno5 + + ! WENO-5重构实现(简化版本) + subroutine weno5_reconstruct(this, q, qL, qR) + class(weno5_reconstructor), intent(in) :: this + real(wp), intent(in) :: q(:) + real(wp), intent(out) :: qL(:), qR(:) + + integer :: i, n + real(wp) :: beta0, beta1, beta2, alpha0, alpha1, alpha2, alpha + real(wp) :: w0, w1, w2, q0, q1, q2 + real(wp) :: v0, v1, v2, v3, v4 + + n = size(q) - 4 ! 需要4个ghost cells + + do i = 3, n+2 + ! 获取模板值 + v0 = q(i-2) + v1 = q(i-1) + v2 = q(i) + v3 = q(i+1) + v4 = q(i+2) + + ! 计算左界面值 qL(i-2) + beta0 = (13.0_wp/12.0_wp)*(v0 - 2.0_wp*v1 + v2)**2 & + + (1.0_wp/4.0_wp)*(v0 - 4.0_wp*v1 + 3.0_wp*v2)**2 + beta1 = (13.0_wp/12.0_wp)*(v1 - 2.0_wp*v2 + v3)**2 & + + (1.0_wp/4.0_wp)*(v1 - v3)**2 + beta2 = (13.0_wp/12.0_wp)*(v2 - 2.0_wp*v3 + v4)**2 & + + (1.0_wp/4.0_wp)*(3.0_wp*v2 - 4.0_wp*v3 + v4)**2 + + alpha0 = 0.1_wp / (this%epsilon + beta0)**2 + alpha1 = 0.6_wp / (this%epsilon + beta1)**2 + alpha2 = 0.3_wp / (this%epsilon + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + + q0 = (1.0_wp/3.0_wp)*v0 - (7.0_wp/6.0_wp)*v1 + (11.0_wp/6.0_wp)*v2 + q1 = (-1.0_wp/6.0_wp)*v1 + (5.0_wp/6.0_wp)*v2 + (1.0_wp/3.0_wp)*v3 + q2 = (1.0_wp/3.0_wp)*v2 + (5.0_wp/6.0_wp)*v3 - (1.0_wp/6.0_wp)*v4 + + qL(i-2) = w0 * q0 + w1 * q1 + w2 * q2 + + ! 计算右界面值 qR(i-2) + ! (使用对称模板) + beta0 = (13.0_wp/12.0_wp)*(v0 - 2.0_wp*v1 + v2)**2 & + + (1.0_wp/4.0_wp)*(v0 - 4.0_wp*v1 + 3.0_wp*v2)**2 + beta1 = (13.0_wp/12.0_wp)*(v1 - 2.0_wp*v2 + v3)**2 & + + (1.0_wp/4.0_wp)*(v1 - v3)**2 + beta2 = (13.0_wp/12.0_wp)*(v2 - 2.0_wp*v3 + v4)**2 & + + (1.0_wp/4.0_wp)*(3.0_wp*v2 - 4.0_wp*v3 + v4)**2 + + alpha0 = 0.3_wp / (this%epsilon + beta0)**2 + alpha1 = 0.6_wp / (this%epsilon + beta1)**2 + alpha2 = 0.1_wp / (this%epsilon + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + + q0 = (-1.0_wp/6.0_wp)*v0 + (5.0_wp/6.0_wp)*v1 + (1.0_wp/3.0_wp)*v2 + q1 = (1.0_wp/3.0_wp)*v1 + (5.0_wp/6.0_wp)*v2 - (1.0_wp/6.0_wp)*v3 + q2 = (11.0_wp/6.0_wp)*v2 - (7.0_wp/6.0_wp)*v3 + (1.0_wp/3.0_wp)*v4 + + qR(i-2) = w0 * q0 + w1 * q1 + w2 * q2 + end do + end subroutine weno5_reconstruct + + subroutine weno5_info(this) + class(weno5_reconstructor), intent(in) :: this + call reconstructor_info(this) + print *, " 类型: WENO-5重构器" + end subroutine weno5_info + +end module weno5_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01b/tests/CMakeLists.txt new file mode 100644 index 00000000..987d62f1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/tests/CMakeLists.txt @@ -0,0 +1,27 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +# Minimal functionality test +add_executable(test_minimal + test_minimal.f90 +) +target_link_libraries(test_minimal + core + infrastructure +) +target_include_directories(test_minimal PRIVATE + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# Test target +add_custom_target(run_tests + COMMAND echo "=== Running Tests ===" + COMMAND echo "Running minimal test..." + COMMAND test_minimal + COMMAND echo "" + COMMAND echo "=== Tests Completed ===" + DEPENDS test_minimal + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) + +message(STATUS "Tests configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/tests/test_factory_system.f90 b/example/1d-linear-convection/weno3/fortran/registry/01b/tests/test_factory_system.f90 new file mode 100644 index 00000000..1ef18c12 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/tests/test_factory_system.f90 @@ -0,0 +1,86 @@ +! tests/test_factory_system.f90 +program test_factory_system + use registry_advanced_module + use eno_reconstructor_module + use rusanov_flux_module + implicit none + + class(*), allocatable :: instance1, instance2 + type(eno_reconstructor), pointer :: eno_ptr + type(rusanov_flux), pointer :: flux_ptr + real(wp), allocatable :: q(:), qL(:), qR(:), flux(:) + integer :: i + + print *, "=== Factory System Test ===" + print *, "" + + ! 注册工厂 + print *, "1. Registering factories..." + call register_factory_with_order("reconstructor", "eno", create_eno, 3) + call register_factory("flux", "rusanov", create_rusanov) + + call component_registry%list_all() + print *, "" + + ! 创建实例 + print *, "2. Creating instances..." + call component_registry%create("reconstructor", "eno", instance1) + call component_registry%create("flux", "rusanov", instance2) + print *, "" + + ! 类型转换和测试 + print *, "3. Testing instances..." + + ! ENO重构器 + select type (inst => instance1) + type is (eno_reconstructor) + eno_ptr => inst + call eno_ptr%info() + + ! 创建测试数据 + allocate(q(6), qL(5), qR(5)) + do i = 1, 6 + q(i) = real(i, wp) + end do + + ! 计算 + call eno_ptr%compute(q, qL, qR) + print *, " q: ", q + print *, " qL: ", qL + print *, " qR: ", qR + + deallocate(q, qL, qR) + class default + print *, "[ERROR] Wrong type for eno_reconstructor" + end select + + print *, "" + + ! Rusanov通量 + select type (inst => instance2) + type is (rusanov_flux) + flux_ptr => inst + call flux_ptr%info() + + ! 创建测试数据 + allocate(qL(5), qR(5), flux(5)) + do i = 1, 5 + qL(i) = real(i, wp) + qR(i) = real(i + 0.5, wp) + end do + + ! 计算 + call flux_ptr%compute(qL, qR, flux) + print *, " qL: ", qL + print *, " qR: ", qR + print *, " flux: ", flux + + deallocate(qL, qR, flux) + class default + print *, "[ERROR] Wrong type for rusanov_flux" + end select + + print *, "" + print *, "=== Factory System Test Complete ===" + +end program test_factory_system \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/tests/test_minimal.f90 b/example/1d-linear-convection/weno3/fortran/registry/01b/tests/test_minimal.f90 new file mode 100644 index 00000000..cf4c93a5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/tests/test_minimal.f90 @@ -0,0 +1,77 @@ +! tests/test_minimal.f90 +program test_minimal + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config%print() + + call config%with_reconstruction("eno", 3) + call config%with_boundary("periodic") + + call config%print() + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", component_registry%size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Minimal test completed successfully ===" + +end program test_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01b/tests/test_registry_basic.f90 b/example/1d-linear-convection/weno3/fortran/registry/01b/tests/test_registry_basic.f90 new file mode 100644 index 00000000..7c960b5e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01b/tests/test_registry_basic.f90 @@ -0,0 +1,68 @@ +! tests/test_registry_basic.f90 +program test_registry_basic + use registry_module + implicit none + + type(component_info) :: info + logical :: found + + print *, "=== 注册系统基础测试 ===" + print *, "" + + ! 1. 初始化 + print *, "1. 初始化注册表" + print *, "----------------" + call initialize_registry(verbose=.true.) + print *, "初始大小: ", component_registry%size() + print *, "" + + ! 2. 注册组件 + print *, "2. 注册组件" + print *, "------------" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "注册后大小: ", component_registry%size() + print *, "" + + ! 3. 查询组件 + print *, "3. 查询组件" + print *, "------------" + + ! 测试存在的组件 + found = has_component("reconstructor", "eno") + print *, "has_component('reconstructor', 'eno') = ", found + + info = component_registry%get("reconstructor", "eno") + print *, "get('reconstructor', 'eno'):" + call info%print() + + ! 测试不存在的组件 + found = has_component("reconstructor", "unknown") + print *, "has_component('reconstructor', 'unknown') = ", found + + info = component_registry%get("reconstructor", "unknown") + print *, "get('reconstructor', 'unknown'):" + call info%print() + print *, "" + + ! 4. 清理 + print *, "4. 清理注册表" + print *, "--------------" + call cleanup_registry() + print *, "清理后大小: ", component_registry%size() + call component_registry%list_all() + + print *, "" + print *, "=== 基础测试完成 ===" + +end program test_registry_basic \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01c/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01c/CMakeLists.txt new file mode 100644 index 00000000..a2fe5bb7 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01c/CMakeLists.txt @@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + +# Set Fortran standard +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +# Module output directory +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) + +# Compiler flags for Intel Fortran +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + # Intel oneAPI/ifx compiler + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS}") + set(CMAKE_Fortran_FLAGS_DEBUG "/debug:full /Od /traceback /check:all /warn:all /fpe:0") + set(CMAKE_Fortran_FLAGS_RELEASE "/O3") + set(CMAKE_Fortran_FLAGS_RELWITHDEBINFO "/O2 /debug:full") +elseif(CMAKE_Fortran_COMPILER_ID MATCHES "GNU") + # GNU gfortran compiler + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -Wall -Wextra") + set(CMAKE_Fortran_FLAGS_DEBUG "-g -O0 -fcheck=all -fbacktrace -ffpe-trap=invalid,zero,overflow") + set(CMAKE_Fortran_FLAGS_RELEASE "-O3 -march=native") + set(CMAKE_Fortran_FLAGS_RELWITHDEBINFO "-O2 -g") +endif() + +# Set default build type +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Build type" FORCE) + message(STATUS "Setting build type to: ${CMAKE_BUILD_TYPE}") +endif() + +message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") + +# Create module directory +file(MAKE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY}) + +# Add subdirectories +add_subdirectory(src) +add_subdirectory(tests) + +# Install directory +set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/install CACHE PATH "Installation prefix") + +message(STATUS "Installation prefix: ${CMAKE_INSTALL_PREFIX}") +message(STATUS "Module directory: ${CMAKE_Fortran_MODULE_DIRECTORY}") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01c/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01c/src/CMakeLists.txt new file mode 100644 index 00000000..ee38952b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01c/src/CMakeLists.txt @@ -0,0 +1,14 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01c/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01c/src/core/CMakeLists.txt new file mode 100644 index 00000000..376dd4fe --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01c/src/core/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/core/CMakeLists.txt +message(STATUS "配置核心模块...") + +add_library(core + registry.f90 + factory_interfaces.f90 +) + +target_include_directories(core PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 安装规则 +install(TARGETS core + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) +install(FILES + ${CMAKE_Fortran_MODULE_DIRECTORY}/registry_module.mod + ${CMAKE_Fortran_MODULE_DIRECTORY}/factory_interfaces.mod + DESTINATION include/fortran_cfd/core +) + +message(STATUS "核心模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01c/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/01c/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..5511c36c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01c/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp + + ! Factory procedure interface (simplified) + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01c/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/01c/src/core/registry.f90 new file mode 100644 index 00000000..4e2b3b22 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01c/src/core/registry.f90 @@ -0,0 +1,311 @@ +! src/core/registry.f90 +module registry_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + + ! Public interface + public :: wp, component_info, component_registry + public :: register_component_simple, initialize_registry, cleanup_registry + public :: has_component, get_available_components + + ! Type definitions + type :: component_info + character(len=32) :: category = "" + character(len=32) :: name = "" + integer :: order = 0 + logical :: has_factory = .false. + contains + procedure :: print => ci_print + end type component_info + + type :: component_registry_type + private + type(component_info), allocatable :: components(:) + integer :: count = 0 + integer :: capacity = 100 + logical :: verbose = .true. + logical :: initialized = .false. + contains + procedure :: register => cr_register + procedure :: get => cr_get + procedure :: list_all => cr_list_all + procedure :: clear => cr_clear + procedure :: size => cr_size + end type component_registry_type + + ! Global registry instance + type(component_registry_type), save :: component_registry + +contains + + ! ==================== PUBLIC API ==================== + + ! Initialize registry + subroutine initialize_registry(initial_capacity, verbose) + integer, optional, intent(in) :: initial_capacity + logical, optional, intent(in) :: verbose + + if (component_registry%initialized) then + if (component_registry%verbose) then + print *, "[INFO] Registry already initialized" + end if + return + end if + + if (present(initial_capacity)) then + component_registry%capacity = max(10, initial_capacity) + end if + + if (present(verbose)) then + component_registry%verbose = verbose + end if + + ! Allocate array + allocate(component_registry%components(component_registry%capacity)) + + component_registry%initialized = .true. + component_registry%count = 0 + + if (component_registry%verbose) then + print *, "[INIT] Registry initialized, capacity:", component_registry%capacity + end if + end subroutine initialize_registry + + ! Cleanup registry + subroutine cleanup_registry + call component_registry%clear() + if (component_registry%verbose) then + print *, "[CLEANUP] Registry cleaned up" + end if + end subroutine cleanup_registry + + ! Simple registration + subroutine register_component_simple(category, name) + character(len=*), intent(in) :: category, name + + type(component_info) :: info + + info%category = to_lower(trim(adjustl(category))) + info%name = to_lower(trim(adjustl(name))) + info%order = 0 + info%has_factory = .false. + + call component_registry%register(info) + end subroutine register_component_simple + + ! Check if component exists + function has_component(category, name) result(found) + character(len=*), intent(in) :: category, name + logical :: found + + type(component_info) :: info + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + info = component_registry%get(cat_lower, name_lower) + found = (len_trim(info%category) > 0) + end function has_component + + ! Get available components in a category + subroutine get_available_components(category, names, orders) + character(len=*), intent(in) :: category + character(len=:), allocatable, intent(out), optional :: names(:) + integer, allocatable, intent(out), optional :: orders(:) + + character(len=32) :: cat_lower + integer :: i, count, idx + type(component_info) :: info + + cat_lower = to_lower(trim(adjustl(category))) + + ! Count components in this category + count = 0 + do i = 1, component_registry%count + if (component_registry%components(i)%category == cat_lower) then + count = count + 1 + end if + end do + + ! Allocate arrays if requested + if (present(names)) then + allocate(character(len=32) :: names(count)) + end if + + if (present(orders)) then + allocate(orders(count)) + end if + + ! Fill arrays + idx = 1 + do i = 1, component_registry%count + if (component_registry%components(i)%category == cat_lower) then + info = component_registry%components(i) + if (present(names)) then + names(idx) = info%name + end if + if (present(orders)) then + orders(idx) = info%order + end if + idx = idx + 1 + end if + end do + end subroutine get_available_components + + ! ==================== COMPONENT INFO METHODS ==================== + + subroutine ci_print(this) + class(component_info), intent(in) :: this + + if (this%order > 0) then + if (this%has_factory) then + print *, " [", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ", has factory)]" + else + print *, " [", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")]" + end if + else + if (this%has_factory) then + print *, " [", trim(this%category), ".", trim(this%name), & + " (has factory)]" + else + print *, " [", trim(this%category), ".", trim(this%name), "]" + end if + end if + end subroutine ci_print + + ! ==================== REGISTRY INTERNAL METHODS ==================== + + subroutine cr_register(this, info) + class(component_registry_type), intent(inout) :: this + type(component_info), intent(in) :: info + + type(component_info), allocatable :: temp(:) + integer :: i + + if (.not. this%initialized) then + error stop "[ERROR] Registry not initialized, call initialize_registry first" + end if + + ! Check if already exists + do i = 1, this%count + if (this%components(i)%category == info%category .and. & + this%components(i)%name == info%name) then + if (this%verbose) then + print *, "[WARN] Overwriting: ", & + trim(info%category), ".", trim(info%name) + end if + this%components(i) = info + return + end if + end do + + ! Expand array if needed + if (this%count >= this%capacity) then + this%capacity = this%capacity * 2 + allocate(temp(this%capacity)) + temp(1:this%count) = this%components(1:this%count) + call move_alloc(temp, this%components) + + if (this%verbose) then + print *, "[INFO] Registry expanded to capacity:", this%capacity + end if + end if + + ! Add component + this%count = this%count + 1 + this%components(this%count) = info + + if (this%verbose) then + print *, "[OK] Registered: ", trim(info%category), ".", trim(info%name) + end if + end subroutine cr_register + + function cr_get(this, category, name) result(info) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + type(component_info) :: info + + integer :: i + + ! Initialize return value as empty + info%category = "" + info%name = "" + info%order = 0 + info%has_factory = .false. + + if (.not. this%initialized) then + return + end if + + do i = 1, this%count + if (this%components(i)%category == category .and. & + this%components(i)%name == name) then + info = this%components(i) + return + end if + end do + end function cr_get + + subroutine cr_list_all(this) + class(component_registry_type), intent(in) :: this + integer :: i + + if (.not. this%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + print *, "=== Registry Contents (", this%count, " components) ===" + + if (this%count == 0) then + print *, " (empty)" + return + end if + + ! Show components grouped by category + do i = 1, this%count + call this%components(i)%print() + end do + + print *, "===========================================" + end subroutine cr_list_all + + subroutine cr_clear(this) + class(component_registry_type), intent(inout) :: this + + if (allocated(this%components)) then + deallocate(this%components) + end if + + this%count = 0 + this%capacity = 100 + this%initialized = .false. + end subroutine cr_clear + + integer function cr_size(this) + class(component_registry_type), intent(in) :: this + cr_size = this%count + end function cr_size + + ! ==================== UTILITY FUNCTIONS ==================== + + function to_lower(str) result(lower_str) + character(len=*), intent(in) :: str + character(len=len(str)) :: lower_str + integer :: i + + do i = 1, len(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + lower_str(i:i) = char(ichar(str(i:i)) + 32) + else + lower_str(i:i) = str(i:i) + end if + end do + end function to_lower + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01c/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01c/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..2b72c548 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01c/src/infrastructure/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "配置基础设施模块...") + +add_library(infrastructure + config.f90 + mesh.f90 +) + +target_link_libraries(infrastructure PUBLIC core) + +target_include_directories(infrastructure PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS infrastructure + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "基础设施模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01c/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/01c/src/infrastructure/config.f90 new file mode 100644 index 00000000..6a646d17 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01c/src/infrastructure/config.f90 @@ -0,0 +1,98 @@ +! src/infrastructure/config.f90 +module config_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, cfd_config, config_print, config_with_reconstruction + + ! CFD configuration type + type :: cfd_config + character(len=20) :: ic_type = "step" + character(len=20) :: recon_scheme = "eno" + character(len=20) :: flux_type = "rusanov" + integer :: rk_order = 1 + real(wp) :: wave_speed = 1.0_wp + real(wp) :: final_time = 0.625_wp + real(wp) :: dt = 0.025_wp + character(len=20) :: boundary_type = "periodic" + real(wp) :: left_boundary_value = 1.0_wp + real(wp) :: right_boundary_value = 2.0_wp + integer :: spatial_order = 2 + logical :: verbose = .true. + end type cfd_config + + ! Interfaces + interface config_print + module procedure config_print_proc + end interface + + interface config_with_reconstruction + module procedure config_with_reconstruction_proc + end interface + +contains + + subroutine config_print_proc(this) + type(cfd_config), intent(in) :: this + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(this%ic_type) + print *, "Reconstruction: ", trim(this%recon_scheme), " (order:", this%spatial_order, ")" + print *, "Flux type: ", trim(this%flux_type) + print *, "Time integration: RK", this%rk_order + print *, "Wave speed: ", this%wave_speed + print *, "Final time: ", this%final_time + print *, "Time step: ", this%dt + print *, "Boundary: ", trim(this%boundary_type) + if (trim(this%boundary_type) == 'dirichlet') then + print *, " Dirichlet values: [", this%left_boundary_value, ", ", & + this%right_boundary_value, "]" + end if + print *, "===============================" + end subroutine config_print_proc + + subroutine config_with_reconstruction_proc(this, scheme, order) + type(cfd_config), intent(inout) :: this + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + character(len=20) :: scheme_lower + + ! Convert to lowercase + scheme_lower = scheme + call to_lower_inplace(scheme_lower) + this%recon_scheme = trim(adjustl(scheme_lower)) + + ! Set order + if (present(order)) then + this%spatial_order = order + else + ! Smart defaults + if (index(this%recon_scheme, 'weno') > 0) then + this%spatial_order = 5 + else if (trim(this%recon_scheme) == 'eno') then + this%spatial_order = 3 + else + error stop "[ERROR] Unsupported reconstruction scheme: " // trim(this%recon_scheme) + end if + end if + + if (this%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(this%recon_scheme), & + " Order: ", this%spatial_order + end if + end subroutine config_with_reconstruction_proc + + subroutine to_lower_inplace(str) + character(len=*), intent(inout) :: str + integer :: i + + do i = 1, len_trim(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + str(i:i) = char(ichar(str(i:i)) + 32) + end if + end do + end subroutine to_lower_inplace + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01c/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/01c/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..65a45dcd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01c/src/infrastructure/mesh.f90 @@ -0,0 +1,74 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, mesh_type, mesh_init, mesh_print_info + + ! 网格类型 + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer :: ncells = 40 + integer :: nnodes + integer :: nx + real(wp), allocatable :: x(:) ! 节点坐标 + real(wp), allocatable :: xcc(:) ! 单元中心坐标 + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer, optional, intent(in) :: ncells + + integer :: i + + ! 设置参数 + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! 计算派生参数 + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! 分配数组 + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! 生成节点坐标 + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! 生成单元中心坐标 + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== 网格信息 ===" + print *, "计算域: [", this%xmin, ", ", this%xmax, "]" + print *, "单元数: ", this%ncells + print *, "节点数: ", this%nnodes + print *, "网格尺寸 dx: ", this%dx + print *, "域长度 L: ", this%L + print *, "==========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01c/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01c/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01c/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01c/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01c/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..b2617f29 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01c/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux + base.f90 +) + +target_link_libraries(flux PUBLIC core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01c/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/01c/src/numerics/flux/base.f90 new file mode 100644 index 00000000..dce85b48 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01c/src/numerics/flux/base.f90 @@ -0,0 +1,24 @@ +! src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp + + ! Base flux calculator type (simplified) + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01c/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01c/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..77808c1a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01c/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor + base.f90 +) + +target_link_libraries(reconstructor PUBLIC core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01c/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/01c/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..c3a1ac8b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01c/src/numerics/reconstructor/base.f90 @@ -0,0 +1,27 @@ +! src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp + + ! Base reconstructor type (simplified) + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01c/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01c/tests/CMakeLists.txt new file mode 100644 index 00000000..987d62f1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01c/tests/CMakeLists.txt @@ -0,0 +1,27 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +# Minimal functionality test +add_executable(test_minimal + test_minimal.f90 +) +target_link_libraries(test_minimal + core + infrastructure +) +target_include_directories(test_minimal PRIVATE + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# Test target +add_custom_target(run_tests + COMMAND echo "=== Running Tests ===" + COMMAND echo "Running minimal test..." + COMMAND test_minimal + COMMAND echo "" + COMMAND echo "=== Tests Completed ===" + DEPENDS test_minimal + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) + +message(STATUS "Tests configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01c/tests/test_minimal.f90 b/example/1d-linear-convection/weno3/fortran/registry/01c/tests/test_minimal.f90 new file mode 100644 index 00000000..bb4b7cda --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01c/tests/test_minimal.f90 @@ -0,0 +1,108 @@ +! tests/test_minimal.f90 +program test_minimal + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i ! Declare loop variable here + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", component_registry%size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components + print *, "5. Testing available components" + print *, "--------------------------------" + + ! Use a separate block to avoid allocation issues + call test_available_components() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Minimal test completed successfully ===" + +contains + + ! Internal subroutine for testing available components + subroutine test_available_components() + character(len=:), allocatable :: names(:) + integer, allocatable :: orders(:) + integer :: j + + call get_available_components("reconstructor", names, orders) + + if (allocated(names)) then + print *, "Reconstructors:" + do j = 1, size(names) + print *, " - ", trim(names(j)) + if (allocated(orders)) then + print *, " Order: ", orders(j) + end if + end do + else + print *, "No reconstructors found" + end if + end subroutine test_available_components + +end program test_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01d/CMakeLists.txt new file mode 100644 index 00000000..a2fe5bb7 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/CMakeLists.txt @@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + +# Set Fortran standard +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +# Module output directory +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) + +# Compiler flags for Intel Fortran +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + # Intel oneAPI/ifx compiler + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS}") + set(CMAKE_Fortran_FLAGS_DEBUG "/debug:full /Od /traceback /check:all /warn:all /fpe:0") + set(CMAKE_Fortran_FLAGS_RELEASE "/O3") + set(CMAKE_Fortran_FLAGS_RELWITHDEBINFO "/O2 /debug:full") +elseif(CMAKE_Fortran_COMPILER_ID MATCHES "GNU") + # GNU gfortran compiler + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -Wall -Wextra") + set(CMAKE_Fortran_FLAGS_DEBUG "-g -O0 -fcheck=all -fbacktrace -ffpe-trap=invalid,zero,overflow") + set(CMAKE_Fortran_FLAGS_RELEASE "-O3 -march=native") + set(CMAKE_Fortran_FLAGS_RELWITHDEBINFO "-O2 -g") +endif() + +# Set default build type +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Build type" FORCE) + message(STATUS "Setting build type to: ${CMAKE_BUILD_TYPE}") +endif() + +message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") + +# Create module directory +file(MAKE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY}) + +# Add subdirectories +add_subdirectory(src) +add_subdirectory(tests) + +# Install directory +set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/install CACHE PATH "Installation prefix") + +message(STATUS "Installation prefix: ${CMAKE_INSTALL_PREFIX}") +message(STATUS "Module directory: ${CMAKE_Fortran_MODULE_DIRECTORY}") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/build.bat b/example/1d-linear-convection/weno3/fortran/registry/01d/build.bat new file mode 100644 index 00000000..dd93a8f0 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/build.bat @@ -0,0 +1,72 @@ +@echo off +echo ========================================= +echo Fortran CFD Project Builder (Batch) +echo ========================================= +echo. + +REM 设置 Intel oneAPI 环境 +echo [1/5] Setting up Intel oneAPI environment... +call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" > nul 2>&1 +if %errorlevel% neq 0 ( + echo [ERROR] Failed to set Intel oneAPI environment + pause + exit /b 1 +) +echo ✓ Intel oneAPI environment set + +REM 清理构建目录 +echo [2/5] Cleaning build directory... +if exist build ( + rmdir /s /q build + echo ✓ Old build directory removed +) else ( + echo - No existing build directory +) +mkdir build +cd build + +REM 配置项目 +echo [3/5] Configuring project with Intel Fortran... +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. +if %errorlevel% neq 0 ( + echo [ERROR] CMake configuration failed + pause + exit /b 1 +) +echo ✓ CMake configuration successful + +REM 构建项目 +echo [4/5] Building project... +cmake --build . --config Debug +if %errorlevel% neq 0 ( + echo [ERROR] Build failed + pause + exit /b 1 +) +echo ✓ Build successful + +REM 运行测试 +echo [5/5] Running tests... +echo ========================================= +if exist "Debug\test_simple.exe" ( + echo [TEST] Simple functionality test... + echo ----------------------------------------- + Debug\test_simple.exe + echo. +) else ( + echo [WARN] test_simple.exe not found +) + +if exist "Debug\test_factory.exe" ( + echo [TEST] Factory pattern test... + echo ----------------------------------------- + Debug\test_factory.exe + echo. +) else ( + echo [WARN] test_factory.exe not found +) + +echo ========================================= +echo Build directory: %CD% +echo ========================================= +pause \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/build.ps1 b/example/1d-linear-convection/weno3/fortran/registry/01d/build.ps1 new file mode 100644 index 00000000..9b14a8df --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/build.ps1 @@ -0,0 +1,137 @@ +# Fortran CFD Project Builder (PowerShell) +Write-Host "=========================================" -ForegroundColor Cyan +Write-Host " Fortran CFD Project Builder (PowerShell)" -ForegroundColor Cyan +Write-Host "=========================================" -ForegroundColor Cyan +Write-Host "" + +# 设置 Intel oneAPI 环境 +Write-Host "[1/5] Setting up Intel oneAPI environment..." -ForegroundColor Yellow +$setvarsPath = "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" +if (-not (Test-Path $setvarsPath)) { + Write-Host "[ERROR] Intel oneAPI setvars.bat not found at: $setvarsPath" -ForegroundColor Red + Write-Host "Please check your Intel oneAPI installation." -ForegroundColor Red + Read-Host "Press Enter to exit" + exit 1 +} + +# 调用 setvars.bat 并设置环境变量 +try { + cmd.exe /c "`"$setvarsPath`" > nul 2>&1 && set" | ForEach-Object { + if ($_ -match '^([^=]+)=(.*)$') { + $name = $matches[1] + $value = $matches[2] + [Environment]::SetEnvironmentVariable($name, $value, 'Process') + } + } + Write-Host "✓ Intel oneAPI environment set" -ForegroundColor Green +} +catch { + Write-Host "[ERROR] Failed to set Intel oneAPI environment: $_" -ForegroundColor Red + Read-Host "Press Enter to exit" + exit 1 +} + +# 清理构建目录 +Write-Host "[2/5] Cleaning build directory..." -ForegroundColor Yellow +if (Test-Path "build") { + Remove-Item -Path "build" -Recurse -Force + Write-Host "✓ Old build directory removed" -ForegroundColor Green +} +else { + Write-Host "- No existing build directory" -ForegroundColor Gray +} + +New-Item -ItemType Directory -Path "build" -Force | Out-Null +Set-Location "build" + +# 配置项目 +Write-Host "[3/5] Configuring project with Intel Fortran..." -ForegroundColor Yellow +$cmakeArgs = @('-G', 'Visual Studio 17 2022', '-A', 'x64', '-T', 'fortran=ifx', '..') +try { + & cmake @cmakeArgs + if ($LASTEXITCODE -ne 0) { + throw "CMake configuration failed with exit code: $LASTEXITCODE" + } + Write-Host "✓ CMake configuration successful" -ForegroundColor Green +} +catch { + Write-Host "[ERROR] $_" -ForegroundColor Red + Read-Host "Press Enter to exit" + exit 1 +} + +# 构建项目 +Write-Host "[4/5] Building project..." -ForegroundColor Yellow +try { + & cmake --build . --config Debug + if ($LASTEXITCODE -ne 0) { + throw "Build failed with exit code: $LASTEXITCODE" + } + Write-Host "✓ Build successful" -ForegroundColor Green +} +catch { + Write-Host "[ERROR] $_" -ForegroundColor Red + Read-Host "Press Enter to exit" + exit 1 +} + +# 运行测试 +Write-Host "[5/5] Running tests..." -ForegroundColor Yellow +Write-Host "=========================================" -ForegroundColor Cyan + +$testResults = @() + +# 运行简单测试 +if (Test-Path "Debug\test_simple.exe") { + Write-Host "[TEST] Simple functionality test..." -ForegroundColor Magenta + Write-Host "-----------------------------------------" -ForegroundColor DarkGray + try { + $output = & "Debug\test_simple.exe" + Write-Host $output + $testResults += @{ Name = "Simple Test"; Status = "PASSED" } + Write-Host "" + } + catch { + Write-Host "[ERROR] Simple test failed: $_" -ForegroundColor Red + $testResults += @{ Name = "Simple Test"; Status = "FAILED" } + } +} +else { + Write-Host "[WARN] test_simple.exe not found" -ForegroundColor Yellow +} + +# 运行工厂测试 +if (Test-Path "Debug\test_factory.exe") { + Write-Host "[TEST] Factory pattern test..." -ForegroundColor Magenta + Write-Host "-----------------------------------------" -ForegroundColor DarkGray + try { + $output = & "Debug\test_factory.exe" + Write-Host $output + $testResults += @{ Name = "Factory Test"; Status = "PASSED" } + Write-Host "" + } + catch { + Write-Host "[ERROR] Factory test failed: $_" -ForegroundColor Red + $testResults += @{ Name = "Factory Test"; Status = "FAILED" } + } +} +else { + Write-Host "[WARN] test_factory.exe not found" -ForegroundColor Yellow +} + +# 显示测试总结 +Write-Host "=========================================" -ForegroundColor Cyan +Write-Host "TEST SUMMARY:" -ForegroundColor Cyan +Write-Host "-----------------------------------------" -ForegroundColor DarkGray +foreach ($test in $testResults) { + $color = if ($test.Status -eq "PASSED") { "Green" } else { "Red" } + Write-Host "$($test.Name): $($test.Status)" -ForegroundColor $color +} + +Write-Host "=========================================" -ForegroundColor Cyan +Write-Host "Build directory: $(Get-Location)" -ForegroundColor Cyan +Write-Host "=========================================" -ForegroundColor Cyan + +if ($testResults.Count -eq 0 -or ($testResults.Status -contains "FAILED")) { + Read-Host "Press Enter to exit" +} \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/build.py b/example/1d-linear-convection/weno3/fortran/registry/01d/build.py new file mode 100644 index 00000000..e390ec8a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/build.py @@ -0,0 +1,234 @@ +#!/usr/bin/env python3 +""" +Fortran CFD Project Builder (Python) +支持 Intel oneAPI 环境的自动化构建脚本 +""" + +import os +import sys +import subprocess +import shutil +from pathlib import Path +from typing import List, Tuple, Optional +import platform + +class Color: + """终端颜色代码""" + HEADER = '\033[95m' + BLUE = '\033[94m' + CYAN = '\033[96m' + GREEN = '\033[92m' + YELLOW = '\033[93m' + RED = '\033[91m' + END = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + +def print_step(step_num: int, total_steps: int, message: str): + """打印步骤信息""" + print(f"{Color.CYAN}[{step_num}/{total_steps}]{Color.END} {message}...") + +def print_success(message: str): + """打印成功信息""" + print(f"{Color.GREEN}✓{Color.END} {message}") + +def print_error(message: str): + """打印错误信息""" + print(f"{Color.RED}[ERROR]{Color.END} {message}") + +def print_warning(message: str): + """打印警告信息""" + print(f"{Color.YELLOW}[WARN]{Color.END} {message}") + +def run_command(cmd: List[str], cwd: Optional[str] = None, check: bool = True) -> Tuple[int, str]: + """运行命令并返回结果""" + print(f"{Color.GRAY}Running: {' '.join(cmd)}{Color.END}") + + try: + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + + if result.stdout: + print(result.stdout) + if result.stderr: + print(f"{Color.YELLOW}{result.stderr}{Color.END}") + + if check and result.returncode != 0: + print_error(f"Command failed with exit code: {result.returncode}") + + return result.returncode, result.stdout + + except FileNotFoundError as e: + print_error(f"Command not found: {cmd[0]}") + if check: + raise + return 1, str(e) + +def setup_intel_environment(): + """设置 Intel oneAPI 环境""" + setvars_path = r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" + + if not os.path.exists(setvars_path): + print_error(f"Intel oneAPI setvars.bat not found at: {setvars_path}") + print("Please check your Intel oneAPI installation.") + return False + + # 创建临时的 batch 文件来设置环境 + temp_bat = "setup_intel_env.bat" + with open(temp_bat, 'w') as f: + f.write(f'@echo off\n') + f.write(f'call "{setvars_path}" > nul 2>&1\n') + f.write(f'set\n') # 输出所有环境变量 + + try: + # 运行 batch 文件并捕获环境变量 + env_result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + shell=True, + encoding='utf-8', + errors='ignore' + ) + + # 解析环境变量并设置 + for line in env_result.stdout.split('\n'): + if '=' in line: + key, value = line.split('=', 1) + os.environ[key.strip()] = value.strip() + + os.remove(temp_bat) + print_success("Intel oneAPI environment set") + return True + + except Exception as e: + print_error(f"Failed to set Intel environment: {e}") + if os.path.exists(temp_bat): + os.remove(temp_bat) + return False + +def main(): + """主函数""" + print(f"{Color.CYAN}{'='*50}{Color.END}") + print(f"{Color.BOLD} Fortran CFD Project Builder (Python){Color.END}") + print(f"{Color.CYAN}{'='*50}{Color.END}\n") + + total_steps = 5 + + # 步骤1: 设置 Intel 环境 + print_step(1, total_steps, "Setting up Intel oneAPI environment") + if not setup_intel_environment(): + input("Press Enter to exit...") + sys.exit(1) + + # 步骤2: 清理构建目录 + print_step(2, total_steps, "Cleaning build directory") + build_dir = Path("build") + if build_dir.exists(): + try: + shutil.rmtree(build_dir) + print_success("Old build directory removed") + except Exception as e: + print_error(f"Failed to remove build directory: {e}") + sys.exit(1) + else: + print("- No existing build directory") + + build_dir.mkdir(exist_ok=True) + os.chdir(build_dir) + + # 步骤3: 配置项目 + print_step(3, total_steps, "Configuring project with Intel Fortran") + cmake_cmd = [ + "cmake", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-T", "fortran=ifx", + ".." + ] + + return_code, _ = run_command(cmake_cmd, check=False) + if return_code != 0: + print_error("CMake configuration failed") + input("Press Enter to exit...") + sys.exit(1) + print_success("CMake configuration successful") + + # 步骤4: 构建项目 + print_step(4, total_steps, "Building project") + build_cmd = ["cmake", "--build", ".", "--config", "Debug"] + + return_code, _ = run_command(build_cmd, check=False) + if return_code != 0: + print_error("Build failed") + input("Press Enter to exit...") + sys.exit(1) + print_success("Build successful") + + # 步骤5: 运行测试 + print_step(5, total_steps, "Running tests") + print(f"{Color.CYAN}{'='*50}{Color.END}") + + test_results = [] + + # 运行简单测试 + test_simple = Path("Debug/test_simple.exe") + if test_simple.exists(): + print(f"{Color.MAGENTA}[TEST] Simple functionality test...{Color.END}") + print(f"{Color.DARK_GRAY}{'-'*50}{Color.END}") + return_code, output = run_command([str(test_simple)], check=False) + if return_code == 0: + print_success("Simple test passed") + test_results.append(("Simple Test", "PASSED", Color.GREEN)) + else: + print_error("Simple test failed") + test_results.append(("Simple Test", "FAILED", Color.RED)) + print() + else: + print_warning("test_simple.exe not found") + + # 运行工厂测试 + test_factory = Path("Debug/test_factory.exe") + if test_factory.exists(): + print(f"{Color.MAGENTA}[TEST] Factory pattern test...{Color.END}") + print(f"{Color.DARK_GRAY}{'-'*50}{Color.END}") + return_code, output = run_command([str(test_factory)], check=False) + if return_code == 0: + print_success("Factory test passed") + test_results.append(("Factory Test", "PASSED", Color.GREEN)) + else: + print_error("Factory test failed") + test_results.append(("Factory Test", "FAILED", Color.RED)) + print() + else: + print_warning("test_factory.exe not found") + + # 显示测试总结 + print(f"{Color.CYAN}{'='*50}{Color.END}") + print(f"{Color.BOLD}TEST SUMMARY:{Color.END}") + print(f"{Color.DARK_GRAY}{'-'*50}{Color.END}") + + for test_name, status, color in test_results: + print(f"{color}{test_name}: {status}{Color.END}") + + print(f"{Color.CYAN}{'='*50}{Color.END}") + print(f"{Color.BOLD}Build directory:{Color.END} {os.getcwd()}") + print(f"{Color.CYAN}{'='*50}{Color.END}") + + # 如果有测试失败,等待用户确认 + if any(status == "FAILED" for _, status, _ in test_results): + input("Press Enter to exit...") + +if __name__ == "__main__": + # 添加颜色支持 + if platform.system() == "Windows": + # Windows 需要启用 ANSI 转义序列 + os.system("") + + main() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/build_config.yaml b/example/1d-linear-convection/weno3/fortran/registry/01d/build_config.yaml new file mode 100644 index 00000000..973c1006 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/build_config.yaml @@ -0,0 +1,29 @@ +# 构建配置文件 +project: + name: "FortranCFD" + version: "1.0.0" + language: "Fortran" + +intel: + setvars_path: "C:/Program Files (x86)/Intel/oneAPI/setvars.bat" + compiler: "ifx" + +cmake: + generator: "Visual Studio 17 2022" + platform: "x64" + toolset: "fortran=ifx" + build_type: "Debug" + source_dir: ".." + +build: + clean_before_build: true + run_tests: true + +tests: + - name: "test_simple" + executable: "Debug/test_simple.exe" + description: "Simple functionality test" + + - name: "test_factory" + executable: "Debug/test_factory.exe" + description: "Factory pattern test" \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/build_with_config.py b/example/1d-linear-convection/weno3/fortran/registry/01d/build_with_config.py new file mode 100644 index 00000000..95b35ae5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/build_with_config.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +""" +配置文件驱动的构建脚本 +""" + +import yaml +import os +import sys +from pathlib import Path +from build import ( # 复用上面的 build.py 中的函数 + Color, print_step, print_success, + print_error, print_warning, run_command +) + +def load_config(config_file="build_config.yaml"): + """加载配置文件""" + try: + with open(config_file, 'r', encoding='utf-8') as f: + config = yaml.safe_load(f) + return config + except FileNotFoundError: + print_error(f"Config file not found: {config_file}") + return None + except yaml.YAMLError as e: + print_error(f"Error parsing config file: {e}") + return None + +def setup_intel_from_config(config): + """根据配置设置 Intel 环境""" + setvars_path = config['intel']['setvars_path'] + + if not os.path.exists(setvars_path): + print_error(f"Intel setvars.bat not found: {setvars_path}") + return False + + # 这里可以复用 build.py 中的 setup_intel_environment 函数 + # 或者简化的版本 + os.environ['PATH'] = f"C:\\Program Files (x86)\\Intel\\oneAPI\\compiler\\2025.2\\bin\\intel64;{os.environ['PATH']}" + return True + +def main(): + """主函数 - 配置文件版本""" + config = load_config() + if not config: + sys.exit(1) + + print(f"{Color.CYAN}{'='*60}{Color.END}") + print(f"{Color.BOLD} {config['project']['name']} v{config['project']['version']} - Builder{Color.END}") + print(f"{Color.CYAN}{'='*60}{Color.END}\n") + + total_steps = 4 + + # 步骤1: 设置环境 + print_step(1, total_steps, f"Setting up {config['intel']['compiler']} compiler") + if not setup_intel_from_config(config): + input("Press Enter to exit...") + sys.exit(1) + print_success(f"{config['intel']['compiler']} environment set") + + # 步骤2: 准备构建目录 + print_step(2, total_steps, "Preparing build directory") + build_dir = Path("build") + + if config['build']['clean_before_build'] and build_dir.exists(): + import shutil + try: + shutil.rmtree(build_dir) + print_success("Cleaned build directory") + except Exception as e: + print_error(f"Failed to clean: {e}") + sys.exit(1) + + build_dir.mkdir(exist_ok=True) + os.chdir(build_dir) + + # 步骤3: 配置和构建 + print_step(3, total_steps, "Configuring and building") + + cmake_cmd = [ + "cmake", + "-G", config['cmake']['generator'], + "-A", config['cmake']['platform'], + "-T", config['cmake']['toolset'], + config['cmake']['source_dir'] + ] + + return_code, _ = run_command(cmake_cmd, check=False) + if return_code != 0: + print_error("CMake configuration failed") + sys.exit(1) + + build_cmd = [ + "cmake", "--build", ".", + "--config", config['cmake']['build_type'] + ] + + return_code, _ = run_command(build_cmd, check=False) + if return_code != 0: + print_error("Build failed") + sys.exit(1) + + print_success("Build completed") + + # 步骤4: 运行测试 + if config['build']['run_tests']: + print_step(4, total_steps, "Running tests") + print(f"{Color.CYAN}{'-'*60}{Color.END}") + + for test in config['tests']: + test_exe = Path(test['executable']) + if test_exe.exists(): + print(f"{Color.MAGENTA}[TEST] {test['description']}...{Color.END}") + return_code, output = run_command([str(test_exe)], check=False) + if return_code == 0: + print_success(f"{test['name']} passed") + else: + print_error(f"{test['name']} failed") + print() + else: + print_warning(f"{test['name']} executable not found: {test_exe}") + + print(f"{Color.CYAN}{'='*60}{Color.END}") + print(f"{Color.BOLD}Build completed successfully!{Color.END}") + print(f"{Color.CYAN}{'='*60}{Color.END}") + +if __name__ == "__main__": + import platform + if platform.system() == "Windows": + os.system("") + main() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/clean_build.bat b/example/1d-linear-convection/weno3/fortran/registry/01d/clean_build.bat new file mode 100644 index 00000000..1ebc2c42 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/clean_build.bat @@ -0,0 +1,48 @@ +@echo off +echo === Clean Build for Fortran CFD === +echo. + +REM Clean build directory +if exist build rmdir /s /q build +if exist modules rmdir /s /q modules +mkdir build +cd build + +echo 1. Configuring project... +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +if errorlevel 1 ( + echo Configuration failed! + pause + exit /b 1 +) + +echo. +echo 2. Building project... +cmake --build . --config Debug + +if errorlevel 1 ( + echo Build failed! + pause + exit /b 1 +) + +echo. +echo 3. Running tests... +if exist "Debug\test_simple.exe" ( + echo "=== Simple Test ===" + Debug\test_simple.exe + echo. +) + +if exist "Debug\test_factory.exe" ( + echo "=== Factory Test ===" + Debug\test_factory.exe +) else ( + echo Test executable not found! +) + +echo. +echo === Build process completed === +echo Build directory: %CD% +pause \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01d/src/CMakeLists.txt new file mode 100644 index 00000000..ee38952b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/src/CMakeLists.txt @@ -0,0 +1,14 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01d/src/core/CMakeLists.txt new file mode 100644 index 00000000..376dd4fe --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/src/core/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/core/CMakeLists.txt +message(STATUS "配置核心模块...") + +add_library(core + registry.f90 + factory_interfaces.f90 +) + +target_include_directories(core PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 安装规则 +install(TARGETS core + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) +install(FILES + ${CMAKE_Fortran_MODULE_DIRECTORY}/registry_module.mod + ${CMAKE_Fortran_MODULE_DIRECTORY}/factory_interfaces.mod + DESTINATION include/fortran_cfd/core +) + +message(STATUS "核心模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/01d/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..5511c36c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp + + ! Factory procedure interface (simplified) + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/01d/src/core/registry.f90 new file mode 100644 index 00000000..5d8ca6df --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/src/core/registry.f90 @@ -0,0 +1,386 @@ +! src/core/registry.f90 +module registry_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use factory_interfaces, only: factory_procedure + implicit none + + private + + ! Public interface + public :: wp, component_info, component_registry + public :: register_component_simple, register_component_with_factory + public :: create_component, initialize_registry, cleanup_registry + public :: has_component, get_available_components + + ! Type definitions + type :: component_info + character(len=32) :: category = "" + character(len=32) :: name = "" + integer :: order = 0 + procedure(factory_procedure), pointer, nopass :: factory => null() + logical :: has_factory = .false. + contains + procedure :: print => ci_print + procedure :: create => ci_create + end type component_info + + type :: component_registry_type + private + type(component_info), allocatable :: components(:) + integer :: count = 0 + integer :: capacity = 100 + logical :: verbose = .true. + logical :: initialized = .false. + contains + procedure :: register => cr_register + procedure :: get => cr_get + procedure :: list_all => cr_list_all + procedure :: clear => cr_clear + procedure :: size => cr_size + end type component_registry_type + + ! Global registry instance + type(component_registry_type), save :: component_registry + +contains + + ! ==================== PUBLIC API ==================== + + ! Initialize registry + subroutine initialize_registry(initial_capacity, verbose) + integer, optional, intent(in) :: initial_capacity + logical, optional, intent(in) :: verbose + + if (component_registry%initialized) then + if (component_registry%verbose) then + print *, "[INFO] Registry already initialized" + end if + return + end if + + if (present(initial_capacity)) then + component_registry%capacity = max(10, initial_capacity) + end if + + if (present(verbose)) then + component_registry%verbose = verbose + end if + + ! Allocate array + allocate(component_registry%components(component_registry%capacity)) + + component_registry%initialized = .true. + component_registry%count = 0 + + if (component_registry%verbose) then + print *, "[INIT] Registry initialized, capacity:", component_registry%capacity + end if + end subroutine initialize_registry + + ! Cleanup registry + subroutine cleanup_registry + call component_registry%clear() + if (component_registry%verbose) then + print *, "[CLEANUP] Registry cleaned up" + end if + end subroutine cleanup_registry + + ! Simple registration (no factory) + subroutine register_component_simple(category, name) + character(len=*), intent(in) :: category, name + + type(component_info) :: info + + info%category = to_lower(trim(adjustl(category))) + info%name = to_lower(trim(adjustl(name))) + info%order = 0 + info%factory => null() + info%has_factory = .false. + + call component_registry%register(info) + end subroutine register_component_simple + + ! Registration with factory procedure + subroutine register_component_with_factory(category, name, factory_proc, order) + character(len=*), intent(in) :: category, name + procedure(factory_procedure) :: factory_proc + integer, optional, intent(in) :: order + + type(component_info) :: info + + info%category = to_lower(trim(adjustl(category))) + info%name = to_lower(trim(adjustl(name))) + + if (present(order)) then + info%order = order + else + info%order = 0 + end if + + info%factory => factory_proc + info%has_factory = .true. + + call component_registry%register(info) + end subroutine register_component_with_factory + + ! Create component instance using factory + subroutine create_component(category, name, instance) + character(len=*), intent(in) :: category, name + class(*), allocatable, intent(out) :: instance + + type(component_info) :: info + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + info = component_registry%get(cat_lower, name_lower) + + if (len_trim(info%category) == 0) then + print *, "[ERROR] Component not found: ", trim(cat_lower), ".", trim(name_lower) + return + end if + + if (.not. info%has_factory) then + print *, "[ERROR] Component has no factory: ", trim(cat_lower), ".", trim(name_lower) + return + end if + + if (.not. associated(info%factory)) then + print *, "[ERROR] Factory procedure not associated: ", trim(cat_lower), ".", trim(name_lower) + return + end if + + call info%create(instance) + end subroutine create_component + + ! Check if component exists + function has_component(category, name) result(found) + character(len=*), intent(in) :: category, name + logical :: found + + type(component_info) :: info + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + info = component_registry%get(cat_lower, name_lower) + found = (len_trim(info%category) > 0) + end function has_component + + ! Get available components in a category + subroutine get_available_components(category, names, orders) + character(len=*), intent(in) :: category + character(len=:), allocatable, intent(out), optional :: names(:) + integer, allocatable, intent(out), optional :: orders(:) + + character(len=32) :: cat_lower + integer :: i, count, idx + type(component_info) :: info + + cat_lower = to_lower(trim(adjustl(category))) + + ! Count components in this category + count = 0 + do i = 1, component_registry%count + if (component_registry%components(i)%category == cat_lower) then + count = count + 1 + end if + end do + + ! Allocate arrays if requested + if (present(names)) then + allocate(character(len=32) :: names(count)) + end if + + if (present(orders)) then + allocate(orders(count)) + end if + + ! Fill arrays + idx = 1 + do i = 1, component_registry%count + if (component_registry%components(i)%category == cat_lower) then + info = component_registry%components(i) + if (present(names)) then + names(idx) = info%name + end if + if (present(orders)) then + orders(idx) = info%order + end if + idx = idx + 1 + end if + end do + end subroutine get_available_components + + ! ==================== COMPONENT INFO METHODS ==================== + + subroutine ci_print(this) + class(component_info), intent(in) :: this + + if (this%order > 0) then + if (this%has_factory) then + print *, " [", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ", has factory)]" + else + print *, " [", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")]" + end if + else + if (this%has_factory) then + print *, " [", trim(this%category), ".", trim(this%name), & + " (has factory)]" + else + print *, " [", trim(this%category), ".", trim(this%name), "]" + end if + end if + end subroutine ci_print + + subroutine ci_create(this, instance) + class(component_info), intent(in) :: this + class(*), allocatable, intent(out) :: instance + + if (.not. this%has_factory) then + error stop "[ERROR] Component has no factory procedure" + end if + + if (.not. associated(this%factory)) then + error stop "[ERROR] Factory procedure not associated" + end if + + call this%factory(instance) + end subroutine ci_create + + ! ==================== REGISTRY INTERNAL METHODS ==================== + + subroutine cr_register(this, info) + class(component_registry_type), intent(inout) :: this + type(component_info), intent(in) :: info + + type(component_info), allocatable :: temp(:) + integer :: i + + if (.not. this%initialized) then + error stop "[ERROR] Registry not initialized, call initialize_registry first" + end if + + ! Check if already exists + do i = 1, this%count + if (this%components(i)%category == info%category .and. & + this%components(i)%name == info%name) then + if (this%verbose) then + print *, "[WARN] Overwriting: ", & + trim(info%category), ".", trim(info%name) + end if + this%components(i) = info + return + end if + end do + + ! Expand array if needed + if (this%count >= this%capacity) then + this%capacity = this%capacity * 2 + allocate(temp(this%capacity)) + temp(1:this%count) = this%components(1:this%count) + call move_alloc(temp, this%components) + + if (this%verbose) then + print *, "[INFO] Registry expanded to capacity:", this%capacity + end if + end if + + ! Add component + this%count = this%count + 1 + this%components(this%count) = info + + if (this%verbose) then + print *, "[OK] Registered: ", trim(info%category), ".", trim(info%name) + end if + end subroutine cr_register + + function cr_get(this, category, name) result(info) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + type(component_info) :: info + + integer :: i + + ! Initialize return value as empty + info%category = "" + info%name = "" + info%order = 0 + info%factory => null() + info%has_factory = .false. + + if (.not. this%initialized) then + return + end if + + do i = 1, this%count + if (this%components(i)%category == category .and. & + this%components(i)%name == name) then + info = this%components(i) + return + end if + end do + end function cr_get + + subroutine cr_list_all(this) + class(component_registry_type), intent(in) :: this + integer :: i + + if (.not. this%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + print *, "=== Registry Contents (", this%count, " components) ===" + + if (this%count == 0) then + print *, " (empty)" + return + end if + + ! Show components grouped by category + do i = 1, this%count + call this%components(i)%print() + end do + + print *, "===========================================" + end subroutine cr_list_all + + subroutine cr_clear(this) + class(component_registry_type), intent(inout) :: this + + if (allocated(this%components)) then + deallocate(this%components) + end if + + this%count = 0 + this%capacity = 100 + this%initialized = .false. + end subroutine cr_clear + + integer function cr_size(this) + class(component_registry_type), intent(in) :: this + cr_size = this%count + end function cr_size + + ! ==================== UTILITY FUNCTIONS ==================== + + function to_lower(str) result(lower_str) + character(len=*), intent(in) :: str + character(len=len(str)) :: lower_str + integer :: i + + do i = 1, len(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + lower_str(i:i) = char(ichar(str(i:i)) + 32) + else + lower_str(i:i) = str(i:i) + end if + end do + end function to_lower + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01d/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..2b72c548 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/src/infrastructure/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "配置基础设施模块...") + +add_library(infrastructure + config.f90 + mesh.f90 +) + +target_link_libraries(infrastructure PUBLIC core) + +target_include_directories(infrastructure PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS infrastructure + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "基础设施模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/01d/src/infrastructure/config.f90 new file mode 100644 index 00000000..6a646d17 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/src/infrastructure/config.f90 @@ -0,0 +1,98 @@ +! src/infrastructure/config.f90 +module config_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, cfd_config, config_print, config_with_reconstruction + + ! CFD configuration type + type :: cfd_config + character(len=20) :: ic_type = "step" + character(len=20) :: recon_scheme = "eno" + character(len=20) :: flux_type = "rusanov" + integer :: rk_order = 1 + real(wp) :: wave_speed = 1.0_wp + real(wp) :: final_time = 0.625_wp + real(wp) :: dt = 0.025_wp + character(len=20) :: boundary_type = "periodic" + real(wp) :: left_boundary_value = 1.0_wp + real(wp) :: right_boundary_value = 2.0_wp + integer :: spatial_order = 2 + logical :: verbose = .true. + end type cfd_config + + ! Interfaces + interface config_print + module procedure config_print_proc + end interface + + interface config_with_reconstruction + module procedure config_with_reconstruction_proc + end interface + +contains + + subroutine config_print_proc(this) + type(cfd_config), intent(in) :: this + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(this%ic_type) + print *, "Reconstruction: ", trim(this%recon_scheme), " (order:", this%spatial_order, ")" + print *, "Flux type: ", trim(this%flux_type) + print *, "Time integration: RK", this%rk_order + print *, "Wave speed: ", this%wave_speed + print *, "Final time: ", this%final_time + print *, "Time step: ", this%dt + print *, "Boundary: ", trim(this%boundary_type) + if (trim(this%boundary_type) == 'dirichlet') then + print *, " Dirichlet values: [", this%left_boundary_value, ", ", & + this%right_boundary_value, "]" + end if + print *, "===============================" + end subroutine config_print_proc + + subroutine config_with_reconstruction_proc(this, scheme, order) + type(cfd_config), intent(inout) :: this + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + character(len=20) :: scheme_lower + + ! Convert to lowercase + scheme_lower = scheme + call to_lower_inplace(scheme_lower) + this%recon_scheme = trim(adjustl(scheme_lower)) + + ! Set order + if (present(order)) then + this%spatial_order = order + else + ! Smart defaults + if (index(this%recon_scheme, 'weno') > 0) then + this%spatial_order = 5 + else if (trim(this%recon_scheme) == 'eno') then + this%spatial_order = 3 + else + error stop "[ERROR] Unsupported reconstruction scheme: " // trim(this%recon_scheme) + end if + end if + + if (this%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(this%recon_scheme), & + " Order: ", this%spatial_order + end if + end subroutine config_with_reconstruction_proc + + subroutine to_lower_inplace(str) + character(len=*), intent(inout) :: str + integer :: i + + do i = 1, len_trim(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + str(i:i) = char(ichar(str(i:i)) + 32) + end if + end do + end subroutine to_lower_inplace + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/01d/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..65a45dcd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/src/infrastructure/mesh.f90 @@ -0,0 +1,74 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, mesh_type, mesh_init, mesh_print_info + + ! 网格类型 + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer :: ncells = 40 + integer :: nnodes + integer :: nx + real(wp), allocatable :: x(:) ! 节点坐标 + real(wp), allocatable :: xcc(:) ! 单元中心坐标 + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer, optional, intent(in) :: ncells + + integer :: i + + ! 设置参数 + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! 计算派生参数 + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! 分配数组 + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! 生成节点坐标 + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! 生成单元中心坐标 + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== 网格信息 ===" + print *, "计算域: [", this%xmin, ", ", this%xmax, "]" + print *, "单元数: ", this%ncells + print *, "节点数: ", this%nnodes + print *, "网格尺寸 dx: ", this%dx + print *, "域长度 L: ", this%L + print *, "==========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01d/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01d/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..ca8122dc --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +add_library(flux + base.f90 + rusanov.f90 +) + +target_link_libraries(flux PUBLIC core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/01d/src/numerics/flux/base.f90 new file mode 100644 index 00000000..bba586a7 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/src/numerics/flux/base.f90 @@ -0,0 +1,35 @@ +! src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, flux_calculator_base + + ! Base flux calculator type + type, abstract :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure(compute_interface), deferred :: compute + procedure :: info => flux_info + end type flux_calculator_base + + abstract interface + subroutine compute_interface(this, qL, qR, flux, wave_speed) + import :: flux_calculator_base, wp + class(flux_calculator_base), intent(in) :: this + real(wp), intent(in) :: qL(:), qR(:) + real(wp), intent(out) :: flux(:) + real(wp), intent(in) :: wave_speed + end subroutine compute_interface + end interface + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/01d/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..dc7ac3f5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/src/numerics/flux/rusanov.f90 @@ -0,0 +1,70 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use flux_base_module, only: flux_calculator_base, wp + use registry_module, only: register_component_with_factory + implicit none + + private + public :: wp, rusanov_flux, create_rusanov + + type, extends(flux_calculator_base) :: rusanov_flux + real(wp) :: wave_speed_default = 1.0_wp + contains + procedure :: compute => rusanov_compute + procedure :: info => rusanov_info + end type rusanov_flux + +contains + + ! Factory function + subroutine create_rusanov(instance) + class(*), allocatable, intent(out) :: instance + type(rusanov_flux), allocatable :: flux + + allocate(flux) + flux%name = "Rusanov" + flux%wave_speed_default = 1.0_wp + + call move_alloc(flux, instance) + end subroutine create_rusanov + + ! Rusanov flux computation for linear convection + subroutine rusanov_compute(this, qL, qR, flux, wave_speed) + class(rusanov_flux), intent(in) :: this + real(wp), intent(in) :: qL(:), qR(:) + real(wp), intent(out) :: flux(:) + real(wp), intent(in) :: wave_speed + + integer :: i, n + real(wp) :: max_speed, fL, fR + + n = size(qL) + + if (size(qR) /= n .or. size(flux) /= n) then + print *, "[ERROR] Array size mismatch in rusanov_compute" + return + end if + + ! Maximum wave speed + max_speed = abs(wave_speed) + + ! Compute Rusanov flux at each interface + do i = 1, n + ! Linear convection flux: f(u) = wave_speed * u + fL = wave_speed * qL(i) + fR = wave_speed * qR(i) + + ! Rusanov flux formula + flux(i) = 0.5_wp * (fL + fR) - 0.5_wp * max_speed * (qR(i) - qL(i)) + end do + end subroutine rusanov_compute + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + call flux_info(this) + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01d/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..9d8f9939 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,21 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +add_library(reconstructor + base.f90 + eno.f90 + weno3.f90 +) + +target_link_libraries(reconstructor PUBLIC core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/01d/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..982cf824 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/src/numerics/reconstructor/base.f90 @@ -0,0 +1,38 @@ +! src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, reconstructor_base + + ! Base reconstructor type + type, abstract :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure(reconstruct_interface), deferred :: reconstruct + procedure :: info => reconstructor_info + end type reconstructor_base + + abstract interface + subroutine reconstruct_interface(this, q, qL, qR) + import :: reconstructor_base, wp + class(reconstructor_base), intent(in) :: this + real(wp), intent(in) :: q(:) + real(wp), intent(out) :: qL(:) + real(wp), intent(out) :: qR(:) + end subroutine reconstruct_interface + end interface + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/01d/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..c117e4b5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,70 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use reconstructor_base_module, only: reconstructor_base, wp + use registry_module, only: register_component_with_factory + implicit none + + private + public :: wp, eno_reconstructor, create_eno + + type, extends(reconstructor_base) :: eno_reconstructor + real(wp) :: epsilon = 1.0e-6_wp + contains + procedure :: reconstruct => eno_reconstruct + procedure :: info => eno_info + end type eno_reconstructor + +contains + + ! Factory function + subroutine create_eno(instance) + class(*), allocatable, intent(out) :: instance + type(eno_reconstructor), allocatable :: eno + + allocate(eno) + eno%name = "ENO" + eno%order = 3 + eno%epsilon = 1.0e-6_wp + + call move_alloc(eno, instance) + end subroutine create_eno + + ! ENO reconstruction (simplified 3rd order) + subroutine eno_reconstruct(this, q, qL, qR) + class(eno_reconstructor), intent(in) :: this + real(wp), intent(in) :: q(:) + real(wp), intent(out) :: qL(:) + real(wp), intent(out) :: qR(:) + + integer :: i, n + + n = size(q) - 1 ! Number of interfaces + + if (n /= size(qL) .or. n /= size(qR)) then + print *, "[ERROR] Array size mismatch in eno_reconstruct" + return + end if + + ! Simple implementation: 3rd order ENO + do i = 1, n + if (i >= 2 .and. i <= n-1) then + ! 3-point stencil for 3rd order + qL(i) = (1.0_wp/3.0_wp)*q(i-1) - (7.0_wp/6.0_wp)*q(i) + (11.0_wp/6.0_wp)*q(i+1) + qR(i) = (-1.0_wp/6.0_wp)*q(i) + (5.0_wp/6.0_wp)*q(i+1) + (1.0_wp/3.0_wp)*q(i+2) + else + ! Use simple upwind near boundaries + qL(i) = q(i) + qR(i) = q(i+1) + end if + end do + end subroutine eno_reconstruct + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + call reconstructor_info(this) + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/01d/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..486771f9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,91 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use reconstructor_base_module, only: reconstructor_base, wp + use registry_module, only: register_component_with_factory + implicit none + + private + public :: wp, weno3_reconstructor, create_weno3 + + type, extends(reconstructor_base) :: weno3_reconstructor + real(wp) :: epsilon = 1.0e-6_wp + contains + procedure :: reconstruct => weno3_reconstruct + procedure :: info => weno3_info + end type weno3_reconstructor + +contains + + ! Factory function + subroutine create_weno3(instance) + class(*), allocatable, intent(out) :: instance + type(weno3_reconstructor), allocatable :: weno3 + + allocate(weno3) + weno3%name = "WENO3" + weno3%order = 3 + weno3%epsilon = 1.0e-6_wp + + call move_alloc(weno3, instance) + end subroutine create_weno3 + + ! WENO-3 reconstruction + subroutine weno3_reconstruct(this, q, qL, qR) + class(weno3_reconstructor), intent(in) :: this + real(wp), intent(in) :: q(:) + real(wp), intent(out) :: qL(:) + real(wp), intent(out) :: qR(:) + + integer :: i, n + real(wp) :: beta0, beta1, alpha0, alpha1, alpha, w0, w1 + real(wp) :: q0, q1 + + n = size(q) - 2 ! Need 2 ghost cells + + if (n /= size(qL) .or. n /= size(qR)) then + print *, "[ERROR] Array size mismatch in weno3_reconstruct" + return + end if + + do i = 2, n+1 + ! Left interface value qL(i-1) + beta0 = (q(i) - q(i-1))**2 + beta1 = (q(i+1) - q(i))**2 + + alpha0 = 1.0_wp/3.0_wp / (this%epsilon + beta0)**2 + alpha1 = 2.0_wp/3.0_wp / (this%epsilon + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + + q0 = -0.5_wp*q(i-1) + 1.5_wp*q(i) + q1 = 0.5_wp*q(i) + 0.5_wp*q(i+1) + + qL(i-1) = w0 * q0 + w1 * q1 + + ! Right interface value qR(i-1) + beta0 = (q(i) - q(i-1))**2 + beta1 = (q(i+1) - q(i))**2 + + alpha0 = 2.0_wp/3.0_wp / (this%epsilon + beta0)**2 + alpha1 = 1.0_wp/3.0_wp / (this%epsilon + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + + q0 = 0.5_wp*q(i-1) + 0.5_wp*q(i) + q1 = 1.5_wp*q(i) - 0.5_wp*q(i+1) + + qR(i-1) = w0 * q0 + w1 * q1 + end do + end subroutine weno3_reconstruct + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + call reconstructor_info(this) + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01d/tests/CMakeLists.txt new file mode 100644 index 00000000..681bfdaa --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/tests/CMakeLists.txt @@ -0,0 +1,45 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +# Simple functionality test +add_executable(test_simple + test_minimal_simple.f90 +) +target_link_libraries(test_simple + core + infrastructure +) +target_include_directories(test_simple PRIVATE + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# Factory pattern test +add_executable(test_factory + test_factory.f90 +) +target_link_libraries(test_factory + core + infrastructure + reconstructor + flux +) +target_include_directories(test_factory PRIVATE + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# Test targets +add_custom_target(run_all_tests + COMMAND echo "=== Running All Tests ===" + COMMAND echo "" + COMMAND echo "1. Simple functionality test..." + COMMAND test_simple + COMMAND echo "" + COMMAND echo "2. Factory pattern test..." + COMMAND test_factory + COMMAND echo "" + COMMAND echo "=== All Tests Completed ===" + DEPENDS test_simple test_factory + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) + +message(STATUS "Tests configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/tests/test_factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/01d/tests/test_factory.f90 new file mode 100644 index 00000000..8968b9c2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/tests/test_factory.f90 @@ -0,0 +1,154 @@ +! tests/test_factory.f90 +program test_factory + use registry_module + use config_module + use mesh_module + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + class(*), allocatable :: instance + class(reconstructor_base), allocatable :: recon + class(flux_calculator_base), allocatable :: flux_calc + real(wp), allocatable :: q(:), qL(:), qR(:), flux(:) + integer :: i, n + + print *, "=== Factory Pattern Test ===" + print *, "" + + ! Initialize systems + call initialize_registry(verbose=.true.) + + ! Register components with factories + print *, "1. Registering components with factories..." + call register_component_with_factory("reconstructor", "eno", create_eno, 3) + call register_component_with_factory("reconstructor", "weno3", create_weno3, 3) + call register_component_with_factory("flux", "rusanov", create_rusanov) + + call component_registry%list_all() + print *, "" + + ! Test creating ENO reconstructor + print *, "2. Creating ENO reconstructor..." + call create_component("reconstructor", "eno", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (eno_reconstructor) + allocate(recon, source=inst) + print *, "ENO reconstructor created successfully" + call recon%info() + print *, "" + + ! Test reconstruction + n = 10 + allocate(q(0:n+1), qL(n), qR(n)) + + ! Initialize test data (sine wave) + do i = 0, n+1 + q(i) = sin(2.0_wp * 3.141592653589793_wp * real(i-1, wp) / real(n, wp)) + end do + + print *, "Testing ENO reconstruction..." + call recon%reconstruct(q, qL, qR) + + print *, "q (internal):" + do i = 1, n + write(*, '(I3, F10.6)') i, q(i) + end do + + print *, "qL (left interface values):" + do i = 1, n + write(*, '(I3, F10.6)') i, qL(i) + end do + + deallocate(q, qL, qR) + class default + print *, "[ERROR] Wrong type for ENO reconstructor" + end select + else + print *, "[ERROR] Failed to create ENO reconstructor" + end if + print *, "" + + ! Test creating WENO3 reconstructor + print *, "3. Creating WENO3 reconstructor..." + if (allocated(instance)) deallocate(instance) + call create_component("reconstructor", "weno3", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (weno3_reconstructor) + if (allocated(recon)) deallocate(recon) + allocate(recon, source=inst) + print *, "WENO3 reconstructor created successfully" + call recon%info() + class default + print *, "[ERROR] Wrong type for WENO3 reconstructor" + end select + else + print *, "[ERROR] Failed to create WENO3 reconstructor" + end if + print *, "" + + ! Test creating Rusanov flux calculator + print *, "4. Creating Rusanov flux calculator..." + if (allocated(instance)) deallocate(instance) + call create_component("flux", "rusanov", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (rusanov_flux) + allocate(flux_calc, source=inst) + print *, "Rusanov flux calculator created successfully" + call flux_calc%info() + print *, "" + + ! Test flux computation + n = 5 + allocate(qL(n), qR(n), flux(n)) + + ! Initialize test data + do i = 1, n + qL(i) = 1.0_wp + 0.1_wp * real(i-1, wp) + qR(i) = 1.0_wp + 0.1_wp * real(i, wp) + end do + + print *, "Testing Rusanov flux computation..." + call flux_calc%compute(qL, qR, flux, 1.0_wp) + + print *, "qL:" + do i = 1, n + write(*, '(I3, F10.6)') i, qL(i) + end do + + print *, "qR:" + do i = 1, n + write(*, '(I3, F10.6)') i, qR(i) + end do + + print *, "Flux:" + do i = 1, n + write(*, '(I3, F10.6)') i, flux(i) + end do + + deallocate(qL, qR, flux) + class default + print *, "[ERROR] Wrong type for Rusanov flux" + end select + else + print *, "[ERROR] Failed to create Rusanov flux calculator" + end if + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Factory pattern test completed successfully ===" + +end program test_factory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/tests/test_minimal.f90 b/example/1d-linear-convection/weno3/fortran/registry/01d/tests/test_minimal.f90 new file mode 100644 index 00000000..bb4b7cda --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/tests/test_minimal.f90 @@ -0,0 +1,108 @@ +! tests/test_minimal.f90 +program test_minimal + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i ! Declare loop variable here + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", component_registry%size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components + print *, "5. Testing available components" + print *, "--------------------------------" + + ! Use a separate block to avoid allocation issues + call test_available_components() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Minimal test completed successfully ===" + +contains + + ! Internal subroutine for testing available components + subroutine test_available_components() + character(len=:), allocatable :: names(:) + integer, allocatable :: orders(:) + integer :: j + + call get_available_components("reconstructor", names, orders) + + if (allocated(names)) then + print *, "Reconstructors:" + do j = 1, size(names) + print *, " - ", trim(names(j)) + if (allocated(orders)) then + print *, " Order: ", orders(j) + end if + end do + else + print *, "No reconstructors found" + end if + end subroutine test_available_components + +end program test_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01d/tests/test_minimal_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/01d/tests/test_minimal_simple.f90 new file mode 100644 index 00000000..bb4b7cda --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01d/tests/test_minimal_simple.f90 @@ -0,0 +1,108 @@ +! tests/test_minimal.f90 +program test_minimal + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i ! Declare loop variable here + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", component_registry%size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components + print *, "5. Testing available components" + print *, "--------------------------------" + + ! Use a separate block to avoid allocation issues + call test_available_components() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Minimal test completed successfully ===" + +contains + + ! Internal subroutine for testing available components + subroutine test_available_components() + character(len=:), allocatable :: names(:) + integer, allocatable :: orders(:) + integer :: j + + call get_available_components("reconstructor", names, orders) + + if (allocated(names)) then + print *, "Reconstructors:" + do j = 1, size(names) + print *, " - ", trim(names(j)) + if (allocated(orders)) then + print *, " Order: ", orders(j) + end if + end do + else + print *, "No reconstructors found" + end if + end subroutine test_available_components + +end program test_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01e/CMakeLists.txt new file mode 100644 index 00000000..02eba69e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/CMakeLists.txt @@ -0,0 +1,43 @@ +# 根目录CMakeLists.txt +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + +# 设置Fortran标准 +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +# 模块输出目录 +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) + +# 编译器选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + set(CMAKE_Fortran_FLAGS_DEBUG "/debug:full /Od /traceback /check:all /warn:all /fpe:0") + set(CMAKE_Fortran_FLAGS_RELEASE "/O3") + set(CMAKE_Fortran_FLAGS_RELWITHDEBINFO "/O2 /debug:full") +endif() + +# 设置默认构建类型 +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Build type" FORCE) + message(STATUS "Setting default build type to: ${CMAKE_BUILD_TYPE}") +endif() + +message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") + +# 确保模块目录存在 +file(MAKE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(src) +add_subdirectory(tests) + +# 安装目录 +set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/install CACHE PATH "Installation prefix") + +message(STATUS "Installation prefix: ${CMAKE_INSTALL_PREFIX}") +message(STATUS "Module directory: ${CMAKE_Fortran_MODULE_DIRECTORY}") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/README.md b/example/1d-linear-convection/weno3/fortran/registry/01e/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/01e/scripts/build.bat new file mode 100644 index 00000000..d243695b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/scripts/build.bat @@ -0,0 +1,26 @@ +@echo off +echo ======================================== +echo Fortran CFD Project Builder +echo (Wrapper for Python script) +echo ======================================== +echo. + +REM 检查Python +where python >nul 2>nul +if %errorlevel% neq 0 ( + echo [ERROR] Python not found. Please install Python 3. + pause + exit /b 1 +) + +REM 运行Python构建脚本 +echo [INFO] Running Python build script... +python build.py %* + +if %errorlevel% neq 0 ( + echo [ERROR] Build failed + pause + exit /b 1 +) + +pause \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/scripts/build.ps1 b/example/1d-linear-convection/weno3/fortran/registry/01e/scripts/build.ps1 new file mode 100644 index 00000000..4497c5eb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/scripts/build.ps1 @@ -0,0 +1,32 @@ +# Fortran CFD Project Builder (PowerShell wrapper) + +Write-Host "========================================" -ForegroundColor Cyan +Write-Host " Fortran CFD Project Builder" -ForegroundColor Cyan +Write-Host " (PowerShell wrapper for Python script)" -ForegroundColor Cyan +Write-Host "========================================" -ForegroundColor Cyan +Write-Host "" + +# 检查Python +$python = Get-Command python -ErrorAction SilentlyContinue +if (-not $python) { + $python = Get-Command python3 -ErrorAction SilentlyContinue +} + +if (-not $python) { + Write-Host "[ERROR] Python not found. Please install Python 3." -ForegroundColor Red + Read-Host "Press Enter to exit" + exit 1 +} + +# 运行Python构建脚本 +Write-Host "[INFO] Running Python build script..." -ForegroundColor Yellow +$argsString = $args -join ' ' +$command = "python build.py $argsString" + +Invoke-Expression $command + +if ($LASTEXITCODE -ne 0) { + Write-Host "[ERROR] Build failed" -ForegroundColor Red + Read-Host "Press Enter to exit" + exit 1 +} \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/01e/scripts/build.py new file mode 100644 index 00000000..50038e86 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/scripts/build.py @@ -0,0 +1,331 @@ +#!/usr/bin/env python3 +""" +Fortran CFD 项目构建脚本 (Python版) +支持 Intel oneAPI 环境的自动化构建 +""" + +import os +import sys +import subprocess +import shutil +from pathlib import Path +import argparse +import platform +import time + +class Colors: + """终端颜色""" + if platform.system() == "Windows": + # Windows 启用 ANSI + os.system("") + + HEADER = '\033[95m' + BLUE = '\033[94m' + CYAN = '\033[96m' + GREEN = '\033[92m' + YELLOW = '\033[93m' + RED = '\033[91m' + END = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + +def print_color(text, color=Colors.END): + """彩色打印""" + print(f"{color}{text}{Colors.END}") + +def print_header(text): + """打印标题""" + print_color("\n" + "="*60, Colors.CYAN) + print_color(f" {text}", Colors.BOLD + Colors.CYAN) + print_color("="*60 + "\n", Colors.CYAN) + +def print_step(step, total, message): + """打印步骤""" + print_color(f"[{step}/{total}] {message}...", Colors.YELLOW) + +def print_success(message): + """打印成功""" + print_color(f"✓ {message}", Colors.GREEN) + +def print_error(message): + """打印错误""" + print_color(f"✗ {message}", Colors.RED) + +def print_warning(message): + """打印警告""" + print_color(f"! {message}", Colors.YELLOW) + +def run_command(cmd, cwd=None, check=True, capture=True): + """运行命令""" + print_color(f" $ {' '.join(cmd)}", Colors.BLUE) + + try: + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=capture, + text=True, + encoding='utf-8', + errors='ignore', + shell=False + ) + + if capture and result.stdout: + print(result.stdout) + if capture and result.stderr: + print_color(result.stderr, Colors.YELLOW) + + if check and result.returncode != 0: + print_error(f"Command failed with exit code: {result.returncode}") + return False + + return True + + except FileNotFoundError as e: + print_error(f"Command not found: {cmd[0]}") + if check: + raise + return False + except Exception as e: + print_error(f"Command execution failed: {e}") + return False + +def setup_intel_environment(): + """设置 Intel oneAPI 环境""" + setvars_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + ] + + setvars_path = None + for path in setvars_paths: + if os.path.exists(path): + setvars_path = path + break + + if not setvars_path: + print_error("Intel oneAPI setvars.bat not found.") + print_warning("Please install Intel oneAPI or update the path in build.py") + return False + + # 创建临时的 batch 文件 + temp_bat = "temp_setvars.bat" + with open(temp_bat, 'w') as f: + f.write(f'@echo off\n') + f.write(f'call "{setvars_path}" > nul 2>&1\n') + f.write(f'set\n') + + try: + # 运行并捕获环境变量 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + shell=True, + encoding='utf-8', + errors='ignore' + ) + + # 解析并设置环境变量 + for line in result.stdout.split('\n'): + if '=' in line: + key, value = line.split('=', 1) + os.environ[key.strip()] = value.strip() + + os.remove(temp_bat) + print_success("Intel oneAPI environment configured") + return True + + except Exception as e: + print_error(f"Failed to setup Intel environment: {e}") + if os.path.exists(temp_bat): + os.remove(temp_bat) + return False + +def build_project(args): + """构建项目主函数""" + start_time = time.time() + + print_header(f"Fortran CFD Project Builder") + print_color(f"Build type: {args.build_type}", Colors.CYAN) + print_color(f"Run tests: {args.run_tests}", Colors.CYAN) + print() + + # 获取项目根目录(脚本所在目录的父目录) + script_dir = Path(__file__).parent + project_root = script_dir.parent + os.chdir(project_root) + + print_color(f"Project root: {project_root}", Colors.BLUE) + + # 步骤1: 设置 Intel 环境 + print_step(1, 4, "Setting up Intel Fortran compiler") + if not setup_intel_environment(): + return False + + # 步骤2: 准备构建目录 + print_step(2, 4, "Preparing build directory") + build_dir = project_root / "build" + + if args.clean and build_dir.exists(): + try: + shutil.rmtree(build_dir) + print_success("Cleaned build directory") + except Exception as e: + print_error(f"Failed to clean build directory: {e}") + if not args.force: + return False + + build_dir.mkdir(exist_ok=True) + os.chdir(build_dir) + + # 步骤3: 配置项目 + print_step(3, 4, "Configuring project") + + cmake_cmd = [ + "cmake", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-T", "fortran=ifx", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ".." + ] + + if not run_command(cmake_cmd, check=not args.force): + if not args.force: + return False + + # 步骤4: 构建项目 + print_step(4, 4, "Building project") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + f"-j{args.jobs}" if args.jobs > 1 else "" + ] + build_cmd = [c for c in build_cmd if c] # 移除空字符串 + + if not run_command(build_cmd, check=not args.force): + if not args.force: + return False + + build_time = time.time() - start_time + print_success(f"Build completed in {build_time:.1f} seconds") + + # 运行测试 + if args.run_tests: + print_header("Running Tests") + run_tests(args.build_type) + + print_header("Build Summary") + print_color(f"Build directory: {build_dir}", Colors.GREEN) + print_color(f"Build type: {args.build_type}", Colors.GREEN) + print_color(f"Total time: {build_time:.1f}s", Colors.GREEN) + + return True + +def run_tests(build_type): + """运行测试""" + tests = [ + ("test_simple", "Simple functionality test"), + ("test_factory", "Factory pattern test"), + ] + + for test_name, description in tests: + test_exe = Path(f"./{build_type}/{test_name}.exe") + + if test_exe.exists(): + print_color(f"\n[TEST] {description}...", Colors.MAGENTA) + print_color("-" * 50, Colors.DARK_GRAY) + + try: + result = subprocess.run( + [str(test_exe)], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + + if result.stdout: + print(result.stdout) + + if result.returncode == 0: + print_success(f"{test_name} passed") + else: + print_error(f"{test_name} failed (exit code: {result.returncode})") + if result.stderr: + print_color(result.stderr, Colors.YELLOW) + + except Exception as e: + print_error(f"{test_name} failed: {e}") + else: + print_warning(f"{test_name}.exe not found at {test_exe}") + +def main(): + """主函数""" + parser = argparse.ArgumentParser(description="Fortran CFD Project Builder") + + parser.add_argument( + "--build-type", + choices=["Debug", "Release", "RelWithDebInfo", "MinSizeRel"], + default="Debug", + help="Build type (default: Debug)" + ) + + parser.add_argument( + "--clean", + action="store_true", + help="Clean build directory before building" + ) + + parser.add_argument( + "--no-tests", + action="store_true", + help="Skip running tests" + ) + + parser.add_argument( + "-j", "--jobs", + type=int, + default=0, + help="Number of parallel jobs (0 = use all cores)" + ) + + parser.add_argument( + "--force", + action="store_true", + help="Continue on errors" + ) + + parser.add_argument( + "--verbose", + action="store_true", + help="Verbose output" + ) + + args = parser.parse_args() + + if args.jobs == 0: + import multiprocessing + args.jobs = multiprocessing.cpu_count() + + args.run_tests = not args.no_tests + + try: + success = build_project(args) + if not success and not args.force: + sys.exit(1) + except KeyboardInterrupt: + print_color("\nBuild interrupted by user", Colors.YELLOW) + sys.exit(1) + except Exception as e: + print_error(f"Unexpected error: {e}") + if args.verbose: + import traceback + traceback.print_exc() + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/01e/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01e/src/CMakeLists.txt new file mode 100644 index 00000000..ee38952b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/src/CMakeLists.txt @@ -0,0 +1,14 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01e/src/core/CMakeLists.txt new file mode 100644 index 00000000..376dd4fe --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/src/core/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/core/CMakeLists.txt +message(STATUS "配置核心模块...") + +add_library(core + registry.f90 + factory_interfaces.f90 +) + +target_include_directories(core PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 安装规则 +install(TARGETS core + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) +install(FILES + ${CMAKE_Fortran_MODULE_DIRECTORY}/registry_module.mod + ${CMAKE_Fortran_MODULE_DIRECTORY}/factory_interfaces.mod + DESTINATION include/fortran_cfd/core +) + +message(STATUS "核心模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/01e/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..19cc2c59 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/01e/src/core/registry.f90 new file mode 100644 index 00000000..f996c89b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/src/core/registry.f90 @@ -0,0 +1,318 @@ +! src/core/registry.f90 +module registry_module + use, intrinsic :: iso_fortran_env, only: real64 + use factory_interfaces, only: factory_procedure + implicit none + + private + + ! Public interface + public :: real64, component_info, component_registry + public :: register_component_simple, initialize_registry, cleanup_registry + public :: has_component, get_available_components + public :: registry_is_initialized, registry_get_size ! 添加公共访问方法 + + ! Type definitions (simplified, no factory for now) + type :: component_info + character(len=32) :: category = "" + character(len=32) :: name = "" + integer :: order = 0 + contains + procedure :: print => ci_print + end type component_info + + type :: component_registry_type + private + type(component_info), allocatable :: components(:) + integer :: count = 0 + integer :: capacity = 100 + logical :: verbose = .true. + logical :: initialized = .false. + contains + procedure :: register => cr_register + procedure :: get => cr_get + procedure :: list_all => cr_list_all + procedure :: clear => cr_clear + procedure :: size => cr_size + procedure :: is_initialized => cr_is_initialized ! 内部方法 + end type component_registry_type + + ! Global registry instance + type(component_registry_type), save :: component_registry + +contains + + ! ==================== PUBLIC API ==================== + + ! Initialize registry + subroutine initialize_registry(initial_capacity, verbose) + integer, optional, intent(in) :: initial_capacity + logical, optional, intent(in) :: verbose + + if (component_registry%initialized) then + if (component_registry%verbose) then + print *, "[INFO] Registry already initialized" + end if + return + end if + + if (present(initial_capacity)) then + component_registry%capacity = max(10, initial_capacity) + end if + + if (present(verbose)) then + component_registry%verbose = verbose + end if + + ! Allocate array + allocate(component_registry%components(component_registry%capacity)) + + component_registry%initialized = .true. + component_registry%count = 0 + + if (component_registry%verbose) then + print *, "[INIT] Registry initialized, capacity:", component_registry%capacity + end if + end subroutine initialize_registry + + ! Cleanup registry + subroutine cleanup_registry + call component_registry%clear() + if (component_registry%verbose) then + print *, "[CLEANUP] Registry cleaned up" + end if + end subroutine cleanup_registry + + ! Simple registration (no factory) + subroutine register_component_simple(category, name) + character(len=*), intent(in) :: category, name + + type(component_info) :: info + + info%category = to_lower(trim(adjustl(category))) + info%name = to_lower(trim(adjustl(name))) + info%order = 0 + + call component_registry%register(info) + end subroutine register_component_simple + + ! Check if component exists + function has_component(category, name) result(found) + character(len=*), intent(in) :: category, name + logical :: found + + type(component_info) :: info + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + info = component_registry%get(cat_lower, name_lower) + found = (len_trim(info%category) > 0) + end function has_component + + ! Get available components in a category + subroutine get_available_components(category, names, orders) + character(len=*), intent(in) :: category + character(len=:), allocatable, intent(out), optional :: names(:) + integer, allocatable, intent(out), optional :: orders(:) + + character(len=32) :: cat_lower + integer :: i, count, idx + type(component_info) :: info + + cat_lower = to_lower(trim(adjustl(category))) + + ! Count components in this category + count = 0 + do i = 1, component_registry%count + if (component_registry%components(i)%category == cat_lower) then + count = count + 1 + end if + end do + + ! Allocate arrays if requested + if (present(names)) then + allocate(character(len=32) :: names(count)) + end if + + if (present(orders)) then + allocate(orders(count)) + end if + + ! Fill arrays + idx = 1 + do i = 1, component_registry%count + if (component_registry%components(i)%category == cat_lower) then + info = component_registry%components(i) + if (present(names)) then + names(idx) = info%name + end if + if (present(orders)) then + orders(idx) = info%order + end if + idx = idx + 1 + end if + end do + end subroutine get_available_components + + ! Public function to check if registry is initialized + function registry_is_initialized() result(is_initialized) + logical :: is_initialized + is_initialized = component_registry%is_initialized() + end function registry_is_initialized + + ! Public function to get registry size + function registry_get_size() result(size_val) + integer :: size_val + size_val = component_registry%size() + end function registry_get_size + + ! ==================== COMPONENT INFO METHODS ==================== + + subroutine ci_print(this) + class(component_info), intent(in) :: this + + if (this%order > 0) then + print *, " [", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")]" + else + print *, " [", trim(this%category), ".", trim(this%name), "]" + end if + end subroutine ci_print + + ! ==================== REGISTRY INTERNAL METHODS ==================== + + subroutine cr_register(this, info) + class(component_registry_type), intent(inout) :: this + type(component_info), intent(in) :: info + + type(component_info), allocatable :: temp(:) + integer :: i + + if (.not. this%initialized) then + error stop "[ERROR] Registry not initialized, call initialize_registry first" + end if + + ! Check if already exists + do i = 1, this%count + if (this%components(i)%category == info%category .and. & + this%components(i)%name == info%name) then + if (this%verbose) then + print *, "[WARN] Overwriting: ", & + trim(info%category), ".", trim(info%name) + end if + this%components(i) = info + return + end if + end do + + ! Expand array if needed + if (this%count >= this%capacity) then + this%capacity = this%capacity * 2 + allocate(temp(this%capacity)) + temp(1:this%count) = this%components(1:this%count) + call move_alloc(temp, this%components) + + if (this%verbose) then + print *, "[INFO] Registry expanded to capacity:", this%capacity + end if + end if + + ! Add component + this%count = this%count + 1 + this%components(this%count) = info + + if (this%verbose) then + print *, "[OK] Registered: ", trim(info%category), ".", trim(info%name) + end if + end subroutine cr_register + + function cr_get(this, category, name) result(info) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + type(component_info) :: info + + integer :: i + + ! Initialize return value as empty + info%category = "" + info%name = "" + info%order = 0 + + if (.not. this%initialized) then + return + end if + + do i = 1, this%count + if (this%components(i)%category == category .and. & + this%components(i)%name == name) then + info = this%components(i) + return + end if + end do + end function cr_get + + subroutine cr_list_all(this) + class(component_registry_type), intent(in) :: this + integer :: i + + if (.not. this%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + print *, "=== Registry Contents (", this%count, " components) ===" + + if (this%count == 0) then + print *, " (empty)" + return + end if + + ! Show components grouped by category + do i = 1, this%count + call this%components(i)%print() + end do + + print *, "===========================================" + end subroutine cr_list_all + + subroutine cr_clear(this) + class(component_registry_type), intent(inout) :: this + + if (allocated(this%components)) then + deallocate(this%components) + end if + + this%count = 0 + this%capacity = 100 + this%initialized = .false. + end subroutine cr_clear + + integer function cr_size(this) + class(component_registry_type), intent(in) :: this + cr_size = this%count + end function cr_size + + logical function cr_is_initialized(this) + class(component_registry_type), intent(in) :: this + cr_is_initialized = this%initialized + end function cr_is_initialized + + ! ==================== UTILITY FUNCTIONS ==================== + + function to_lower(str) result(lower_str) + character(len=*), intent(in) :: str + character(len=len(str)) :: lower_str + integer :: i + + do i = 1, len(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + lower_str(i:i) = char(ichar(str(i:i)) + 32) + else + lower_str(i:i) = str(i:i) + end if + end do + end function to_lower + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01e/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..2b72c548 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/src/infrastructure/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "配置基础设施模块...") + +add_library(infrastructure + config.f90 + mesh.f90 +) + +target_link_libraries(infrastructure PUBLIC core) + +target_include_directories(infrastructure PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS infrastructure + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "基础设施模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/01e/src/infrastructure/config.f90 new file mode 100644 index 00000000..34019f2f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/src/infrastructure/config.f90 @@ -0,0 +1,90 @@ +! src/infrastructure/config.f90 +module config_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, cfd_config, config_print, config_with_reconstruction + + ! CFD configuration type + type :: cfd_config + character(len=20) :: ic_type = "step" + character(len=20) :: recon_scheme = "eno" + character(len=20) :: flux_type = "rusanov" + integer :: rk_order = 1 + real(real64) :: wave_speed = 1.0_real64 + real(real64) :: final_time = 0.625_real64 + real(real64) :: dt = 0.025_real64 + character(len=20) :: boundary_type = "periodic" + real(real64) :: left_boundary_value = 1.0_real64 + real(real64) :: right_boundary_value = 2.0_real64 + integer :: spatial_order = 2 + logical :: verbose = .true. + end type cfd_config + +contains + + subroutine config_print(this) + type(cfd_config), intent(in) :: this + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(this%ic_type) + print *, "Reconstruction: ", trim(this%recon_scheme), " (order:", this%spatial_order, ")" + print *, "Flux type: ", trim(this%flux_type) + print *, "Time integration: RK", this%rk_order + print *, "Wave speed: ", this%wave_speed + print *, "Final time: ", this%final_time + print *, "Time step: ", this%dt + print *, "Boundary: ", trim(this%boundary_type) + if (trim(this%boundary_type) == 'dirichlet') then + print *, " Dirichlet values: [", this%left_boundary_value, ", ", & + this%right_boundary_value, "]" + end if + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(this, scheme, order) + type(cfd_config), intent(inout) :: this + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + character(len=20) :: scheme_lower + + ! Convert to lowercase + scheme_lower = scheme + call to_lower_inplace(scheme_lower) + this%recon_scheme = trim(adjustl(scheme_lower)) + + ! Set order + if (present(order)) then + this%spatial_order = order + else + ! Smart defaults + if (index(this%recon_scheme, 'weno') > 0) then + this%spatial_order = 5 + else if (trim(this%recon_scheme) == 'eno') then + this%spatial_order = 3 + else + print *, "[ERROR] Unsupported reconstruction scheme: ", trim(this%recon_scheme) + return + end if + end if + + if (this%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(this%recon_scheme), & + " Order: ", this%spatial_order + end if + end subroutine config_with_reconstruction + + subroutine to_lower_inplace(str) + character(len=*), intent(inout) :: str + integer :: i + + do i = 1, len_trim(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + str(i:i) = char(ichar(str(i:i)) + 32) + end if + end do + end subroutine to_lower_inplace + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/01e/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..65a45dcd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/src/infrastructure/mesh.f90 @@ -0,0 +1,74 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, mesh_type, mesh_init, mesh_print_info + + ! 网格类型 + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer :: ncells = 40 + integer :: nnodes + integer :: nx + real(wp), allocatable :: x(:) ! 节点坐标 + real(wp), allocatable :: xcc(:) ! 单元中心坐标 + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer, optional, intent(in) :: ncells + + integer :: i + + ! 设置参数 + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! 计算派生参数 + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! 分配数组 + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! 生成节点坐标 + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! 生成单元中心坐标 + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== 网格信息 ===" + print *, "计算域: [", this%xmin, ", ", this%xmax, "]" + print *, "单元数: ", this%ncells + print *, "节点数: ", this%nnodes + print *, "网格尺寸 dx: ", this%dx + print *, "域长度 L: ", this%L + print *, "==========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01e/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01e/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..b2617f29 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux + base.f90 +) + +target_link_libraries(flux PUBLIC core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/01e/src/numerics/flux/base.f90 new file mode 100644 index 00000000..d9bd640c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/src/numerics/flux/base.f90 @@ -0,0 +1,24 @@ +! src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/01e/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..414f783d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/src/numerics/flux/rusanov.f90 @@ -0,0 +1,25 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + +contains + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + call flux_info(this) + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01e/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..77808c1a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor + base.f90 +) + +target_link_libraries(reconstructor PUBLIC core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/01e/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..8d3119ee --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/src/numerics/reconstructor/base.f90 @@ -0,0 +1,27 @@ +! src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/01e/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..19029b6d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,25 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + +contains + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + call reconstructor_info(this) + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/01e/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..4f268ac9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,25 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno3_reconstructor + + type, extends(reconstructor_base) :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + +contains + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + call reconstructor_info(this) + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01e/tests/CMakeLists.txt new file mode 100644 index 00000000..a9858189 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/tests/CMakeLists.txt @@ -0,0 +1,16 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +# Simple functionality test +add_executable(test_simple + test_minimal_simple.f90 +) +target_link_libraries(test_simple + core + infrastructure +) +target_include_directories(test_simple PRIVATE + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Tests configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/tests/test_factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/01e/tests/test_factory.f90 new file mode 100644 index 00000000..8968b9c2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/tests/test_factory.f90 @@ -0,0 +1,154 @@ +! tests/test_factory.f90 +program test_factory + use registry_module + use config_module + use mesh_module + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + class(*), allocatable :: instance + class(reconstructor_base), allocatable :: recon + class(flux_calculator_base), allocatable :: flux_calc + real(wp), allocatable :: q(:), qL(:), qR(:), flux(:) + integer :: i, n + + print *, "=== Factory Pattern Test ===" + print *, "" + + ! Initialize systems + call initialize_registry(verbose=.true.) + + ! Register components with factories + print *, "1. Registering components with factories..." + call register_component_with_factory("reconstructor", "eno", create_eno, 3) + call register_component_with_factory("reconstructor", "weno3", create_weno3, 3) + call register_component_with_factory("flux", "rusanov", create_rusanov) + + call component_registry%list_all() + print *, "" + + ! Test creating ENO reconstructor + print *, "2. Creating ENO reconstructor..." + call create_component("reconstructor", "eno", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (eno_reconstructor) + allocate(recon, source=inst) + print *, "ENO reconstructor created successfully" + call recon%info() + print *, "" + + ! Test reconstruction + n = 10 + allocate(q(0:n+1), qL(n), qR(n)) + + ! Initialize test data (sine wave) + do i = 0, n+1 + q(i) = sin(2.0_wp * 3.141592653589793_wp * real(i-1, wp) / real(n, wp)) + end do + + print *, "Testing ENO reconstruction..." + call recon%reconstruct(q, qL, qR) + + print *, "q (internal):" + do i = 1, n + write(*, '(I3, F10.6)') i, q(i) + end do + + print *, "qL (left interface values):" + do i = 1, n + write(*, '(I3, F10.6)') i, qL(i) + end do + + deallocate(q, qL, qR) + class default + print *, "[ERROR] Wrong type for ENO reconstructor" + end select + else + print *, "[ERROR] Failed to create ENO reconstructor" + end if + print *, "" + + ! Test creating WENO3 reconstructor + print *, "3. Creating WENO3 reconstructor..." + if (allocated(instance)) deallocate(instance) + call create_component("reconstructor", "weno3", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (weno3_reconstructor) + if (allocated(recon)) deallocate(recon) + allocate(recon, source=inst) + print *, "WENO3 reconstructor created successfully" + call recon%info() + class default + print *, "[ERROR] Wrong type for WENO3 reconstructor" + end select + else + print *, "[ERROR] Failed to create WENO3 reconstructor" + end if + print *, "" + + ! Test creating Rusanov flux calculator + print *, "4. Creating Rusanov flux calculator..." + if (allocated(instance)) deallocate(instance) + call create_component("flux", "rusanov", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (rusanov_flux) + allocate(flux_calc, source=inst) + print *, "Rusanov flux calculator created successfully" + call flux_calc%info() + print *, "" + + ! Test flux computation + n = 5 + allocate(qL(n), qR(n), flux(n)) + + ! Initialize test data + do i = 1, n + qL(i) = 1.0_wp + 0.1_wp * real(i-1, wp) + qR(i) = 1.0_wp + 0.1_wp * real(i, wp) + end do + + print *, "Testing Rusanov flux computation..." + call flux_calc%compute(qL, qR, flux, 1.0_wp) + + print *, "qL:" + do i = 1, n + write(*, '(I3, F10.6)') i, qL(i) + end do + + print *, "qR:" + do i = 1, n + write(*, '(I3, F10.6)') i, qR(i) + end do + + print *, "Flux:" + do i = 1, n + write(*, '(I3, F10.6)') i, flux(i) + end do + + deallocate(qL, qR, flux) + class default + print *, "[ERROR] Wrong type for Rusanov flux" + end select + else + print *, "[ERROR] Failed to create Rusanov flux calculator" + end if + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Factory pattern test completed successfully ===" + +end program test_factory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/tests/test_minimal.f90 b/example/1d-linear-convection/weno3/fortran/registry/01e/tests/test_minimal.f90 new file mode 100644 index 00000000..bb4b7cda --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/tests/test_minimal.f90 @@ -0,0 +1,108 @@ +! tests/test_minimal.f90 +program test_minimal + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i ! Declare loop variable here + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", component_registry%size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components + print *, "5. Testing available components" + print *, "--------------------------------" + + ! Use a separate block to avoid allocation issues + call test_available_components() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Minimal test completed successfully ===" + +contains + + ! Internal subroutine for testing available components + subroutine test_available_components() + character(len=:), allocatable :: names(:) + integer, allocatable :: orders(:) + integer :: j + + call get_available_components("reconstructor", names, orders) + + if (allocated(names)) then + print *, "Reconstructors:" + do j = 1, size(names) + print *, " - ", trim(names(j)) + if (allocated(orders)) then + print *, " Order: ", orders(j) + end if + end do + else + print *, "No reconstructors found" + end if + end subroutine test_available_components + +end program test_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01e/tests/test_minimal_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/01e/tests/test_minimal_simple.f90 new file mode 100644 index 00000000..689165de --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01e/tests/test_minimal_simple.f90 @@ -0,0 +1,84 @@ +! tests/test_minimal_simple.f90 +program test_minimal_simple + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Simple Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components (simple version) + print *, "5. Testing registry functionality" + print *, "---------------------------------" + print *, "Registry initialized: ", registry_is_initialized() + print *, "Number of components: ", registry_get_size() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Simple test completed successfully ===" + +end program test_minimal_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01f/CMakeLists.txt new file mode 100644 index 00000000..02eba69e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/CMakeLists.txt @@ -0,0 +1,43 @@ +# 根目录CMakeLists.txt +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + +# 设置Fortran标准 +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +# 模块输出目录 +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) + +# 编译器选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + set(CMAKE_Fortran_FLAGS_DEBUG "/debug:full /Od /traceback /check:all /warn:all /fpe:0") + set(CMAKE_Fortran_FLAGS_RELEASE "/O3") + set(CMAKE_Fortran_FLAGS_RELWITHDEBINFO "/O2 /debug:full") +endif() + +# 设置默认构建类型 +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Build type" FORCE) + message(STATUS "Setting default build type to: ${CMAKE_BUILD_TYPE}") +endif() + +message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") + +# 确保模块目录存在 +file(MAKE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(src) +add_subdirectory(tests) + +# 安装目录 +set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/install CACHE PATH "Installation prefix") + +message(STATUS "Installation prefix: ${CMAKE_INSTALL_PREFIX}") +message(STATUS "Module directory: ${CMAKE_Fortran_MODULE_DIRECTORY}") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/README.md b/example/1d-linear-convection/weno3/fortran/registry/01f/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/01f/scripts/build.bat new file mode 100644 index 00000000..d243695b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/scripts/build.bat @@ -0,0 +1,26 @@ +@echo off +echo ======================================== +echo Fortran CFD Project Builder +echo (Wrapper for Python script) +echo ======================================== +echo. + +REM 检查Python +where python >nul 2>nul +if %errorlevel% neq 0 ( + echo [ERROR] Python not found. Please install Python 3. + pause + exit /b 1 +) + +REM 运行Python构建脚本 +echo [INFO] Running Python build script... +python build.py %* + +if %errorlevel% neq 0 ( + echo [ERROR] Build failed + pause + exit /b 1 +) + +pause \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/scripts/build.ps1 b/example/1d-linear-convection/weno3/fortran/registry/01f/scripts/build.ps1 new file mode 100644 index 00000000..4497c5eb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/scripts/build.ps1 @@ -0,0 +1,32 @@ +# Fortran CFD Project Builder (PowerShell wrapper) + +Write-Host "========================================" -ForegroundColor Cyan +Write-Host " Fortran CFD Project Builder" -ForegroundColor Cyan +Write-Host " (PowerShell wrapper for Python script)" -ForegroundColor Cyan +Write-Host "========================================" -ForegroundColor Cyan +Write-Host "" + +# 检查Python +$python = Get-Command python -ErrorAction SilentlyContinue +if (-not $python) { + $python = Get-Command python3 -ErrorAction SilentlyContinue +} + +if (-not $python) { + Write-Host "[ERROR] Python not found. Please install Python 3." -ForegroundColor Red + Read-Host "Press Enter to exit" + exit 1 +} + +# 运行Python构建脚本 +Write-Host "[INFO] Running Python build script..." -ForegroundColor Yellow +$argsString = $args -join ' ' +$command = "python build.py $argsString" + +Invoke-Expression $command + +if ($LASTEXITCODE -ne 0) { + Write-Host "[ERROR] Build failed" -ForegroundColor Red + Read-Host "Press Enter to exit" + exit 1 +} \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/01f/scripts/build.py new file mode 100644 index 00000000..d632c338 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/scripts/build.py @@ -0,0 +1,355 @@ +#!/usr/bin/env python3 +""" +Fortran CFD 项目构建脚本 (Python版) +支持 Intel oneAPI 环境的自动化构建 +""" + +import os +import sys +import subprocess +import shutil +from pathlib import Path +import argparse +import platform +import time + +class Colors: + """终端颜色""" + if platform.system() == "Windows": + # Windows 启用 ANSI + os.system("") + + HEADER = '\033[95m' + BLUE = '\033[94m' + CYAN = '\033[96m' + GREEN = '\033[92m' + YELLOW = '\033[93m' + RED = '\033[91m' + MAGENTA = '\033[95m' + DARK_GRAY = '\033[90m' # 添加这个 + END = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + +def print_color(text, color=Colors.END): + """彩色打印""" + print(f"{color}{text}{Colors.END}") + +def print_header(text): + """打印标题""" + print_color("\n" + "="*60, Colors.CYAN) + print_color(f" {text}", Colors.BOLD + Colors.CYAN) + print_color("="*60 + "\n", Colors.CYAN) + +def print_step(step, total, message): + """打印步骤""" + print_color(f"[{step}/{total}] {message}...", Colors.YELLOW) + +def print_success(message): + """打印成功""" + print_color(f"✓ {message}", Colors.GREEN) + +def print_error(message): + """打印错误""" + print_color(f"✗ {message}", Colors.RED) + +def print_warning(message): + """打印警告""" + print_color(f"! {message}", Colors.YELLOW) + +def run_command(cmd, cwd=None, check=True, capture=True): + """运行命令""" + print_color(f" $ {' '.join(cmd)}", Colors.BLUE) + + try: + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=capture, + text=True, + encoding='utf-8', + errors='ignore', + shell=False + ) + + if capture and result.stdout: + print(result.stdout) + if capture and result.stderr: + print_color(result.stderr, Colors.YELLOW) + + if check and result.returncode != 0: + print_error(f"Command failed with exit code: {result.returncode}") + return False + + return True + + except FileNotFoundError as e: + print_error(f"Command not found: {cmd[0]}") + if check: + raise + return False + except Exception as e: + print_error(f"Command execution failed: {e}") + return False + +def setup_intel_environment(): + """设置 Intel oneAPI 环境""" + setvars_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + ] + + setvars_path = None + for path in setvars_paths: + if os.path.exists(path): + setvars_path = path + break + + if not setvars_path: + print_error("Intel oneAPI setvars.bat not found.") + print_warning("Please install Intel oneAPI or update the path in build.py") + return False + + # 创建临时的 batch 文件 + temp_bat = "temp_setvars.bat" + with open(temp_bat, 'w') as f: + f.write(f'@echo off\n') + f.write(f'call "{setvars_path}" > nul 2>&1\n') + f.write(f'set\n') + + try: + # 运行并捕获环境变量 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + shell=True, + encoding='utf-8', + errors='ignore' + ) + + # 解析并设置环境变量 + for line in result.stdout.split('\n'): + if '=' in line: + key, value = line.split('=', 1) + os.environ[key.strip()] = value.strip() + + os.remove(temp_bat) + print_success("Intel oneAPI environment configured") + return True + + except Exception as e: + print_error(f"Failed to setup Intel environment: {e}") + if os.path.exists(temp_bat): + os.remove(temp_bat) + return False + +def build_project(args): + """构建项目主函数""" + start_time = time.time() + + print_header(f"Fortran CFD Project Builder") + print_color(f"Build type: {args.build_type}", Colors.CYAN) + print_color(f"Run tests: {args.run_tests}", Colors.CYAN) + print() + + # 获取项目根目录(脚本所在目录的父目录) + script_dir = Path(__file__).parent + project_root = script_dir.parent + os.chdir(project_root) + + print_color(f"Project root: {project_root}", Colors.BLUE) + + # 步骤1: 设置 Intel 环境 + print_step(1, 4, "Setting up Intel Fortran compiler") + if not setup_intel_environment(): + return False + + # 步骤2: 准备构建目录 + print_step(2, 4, "Preparing build directory") + build_dir = project_root / "build" + + if args.clean and build_dir.exists(): + try: + shutil.rmtree(build_dir) + print_success("Cleaned build directory") + except Exception as e: + print_error(f"Failed to clean build directory: {e}") + if not args.force: + return False + + build_dir.mkdir(exist_ok=True) + os.chdir(build_dir) + + # 步骤3: 配置项目 + print_step(3, 4, "Configuring project") + + cmake_cmd = [ + "cmake", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-T", "fortran=ifx", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ".." + ] + + if not run_command(cmake_cmd, check=not args.force): + if not args.force: + return False + + # 步骤4: 构建项目 + print_step(4, 4, "Building project") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + f"-j{args.jobs}" if args.jobs > 1 else "" + ] + build_cmd = [c for c in build_cmd if c] # 移除空字符串 + + if not run_command(build_cmd, check=not args.force): + if not args.force: + return False + + build_time = time.time() - start_time + print_success(f"Build completed in {build_time:.1f} seconds") + + # 运行测试 + if args.run_tests: + print_header("Running Tests") + run_tests(args.build_type) + + print_header("Build Summary") + print_color(f"Build directory: {build_dir}", Colors.GREEN) + print_color(f"Build type: {args.build_type}", Colors.GREEN) + print_color(f"Total time: {build_time:.1f}s", Colors.GREEN) + + return True + +def run_tests(build_type): + """运行测试""" + tests = [ + ("test_simple", "Simple functionality test"), + ("test_factory", "Factory pattern test"), + ] + + for test_name, description in tests: + # 修复:在这里直接访问当前循环的 test_name + _run_single_test(test_name, description, build_type) + +def _run_single_test(test_name, description, build_type): + """运行单个测试(修复作用域问题)""" + # 可能的测试可执行文件路径 + possible_paths = [ + Path(f"./{build_type}/{test_name}.exe"), + Path(f"./tests/{build_type}/{test_name}.exe"), + Path(f"./tests/{test_name}.exe"), + Path(f"{test_name}.exe"), + Path(f"./{test_name}.exe"), + ] + + test_exe = None + + # 尝试所有可能的路径 + for path in possible_paths: + if path.exists(): + test_exe = path + break + + if test_exe: + print_color(f"\n[TEST] {description}...", Colors.MAGENTA) + print_color("-" * 50, Colors.DARK_GRAY) + + try: + result = subprocess.run( + [str(test_exe)], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + + if result.stdout: + print(result.stdout) + + if result.returncode == 0: + print_success(f"{test_name} passed") + else: + print_error(f"{test_name} failed (exit code: {result.returncode})") + if result.stderr: + print_color(result.stderr, Colors.YELLOW) + + except Exception as e: + print_error(f"{test_name} failed: {e}") + else: + print_warning(f"{test_name}.exe not found. Searched paths:") + for path in possible_paths: + print_color(f" - {path}", Colors.YELLOW) + +def main(): + """主函数""" + parser = argparse.ArgumentParser(description="Fortran CFD Project Builder") + + parser.add_argument( + "--build-type", + choices=["Debug", "Release", "RelWithDebInfo", "MinSizeRel"], + default="Debug", + help="Build type (default: Debug)" + ) + + parser.add_argument( + "--clean", + action="store_true", + help="Clean build directory before building" + ) + + parser.add_argument( + "--no-tests", + action="store_true", + help="Skip running tests" + ) + + parser.add_argument( + "-j", "--jobs", + type=int, + default=0, + help="Number of parallel jobs (0 = use all cores)" + ) + + parser.add_argument( + "--force", + action="store_true", + help="Continue on errors" + ) + + parser.add_argument( + "--verbose", + action="store_true", + help="Verbose output" + ) + + args = parser.parse_args() + + if args.jobs == 0: + import multiprocessing + args.jobs = multiprocessing.cpu_count() + + args.run_tests = not args.no_tests + + try: + success = build_project(args) + if not success and not args.force: + sys.exit(1) + except KeyboardInterrupt: + print_color("\nBuild interrupted by user", Colors.YELLOW) + sys.exit(1) + except Exception as e: + print_error(f"Unexpected error: {e}") + if args.verbose: + import traceback + traceback.print_exc() + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/01f/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01f/src/CMakeLists.txt new file mode 100644 index 00000000..ee38952b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/src/CMakeLists.txt @@ -0,0 +1,14 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01f/src/core/CMakeLists.txt new file mode 100644 index 00000000..376dd4fe --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/src/core/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/core/CMakeLists.txt +message(STATUS "配置核心模块...") + +add_library(core + registry.f90 + factory_interfaces.f90 +) + +target_include_directories(core PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 安装规则 +install(TARGETS core + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) +install(FILES + ${CMAKE_Fortran_MODULE_DIRECTORY}/registry_module.mod + ${CMAKE_Fortran_MODULE_DIRECTORY}/factory_interfaces.mod + DESTINATION include/fortran_cfd/core +) + +message(STATUS "核心模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/01f/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..19cc2c59 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/01f/src/core/registry.f90 new file mode 100644 index 00000000..f996c89b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/src/core/registry.f90 @@ -0,0 +1,318 @@ +! src/core/registry.f90 +module registry_module + use, intrinsic :: iso_fortran_env, only: real64 + use factory_interfaces, only: factory_procedure + implicit none + + private + + ! Public interface + public :: real64, component_info, component_registry + public :: register_component_simple, initialize_registry, cleanup_registry + public :: has_component, get_available_components + public :: registry_is_initialized, registry_get_size ! 添加公共访问方法 + + ! Type definitions (simplified, no factory for now) + type :: component_info + character(len=32) :: category = "" + character(len=32) :: name = "" + integer :: order = 0 + contains + procedure :: print => ci_print + end type component_info + + type :: component_registry_type + private + type(component_info), allocatable :: components(:) + integer :: count = 0 + integer :: capacity = 100 + logical :: verbose = .true. + logical :: initialized = .false. + contains + procedure :: register => cr_register + procedure :: get => cr_get + procedure :: list_all => cr_list_all + procedure :: clear => cr_clear + procedure :: size => cr_size + procedure :: is_initialized => cr_is_initialized ! 内部方法 + end type component_registry_type + + ! Global registry instance + type(component_registry_type), save :: component_registry + +contains + + ! ==================== PUBLIC API ==================== + + ! Initialize registry + subroutine initialize_registry(initial_capacity, verbose) + integer, optional, intent(in) :: initial_capacity + logical, optional, intent(in) :: verbose + + if (component_registry%initialized) then + if (component_registry%verbose) then + print *, "[INFO] Registry already initialized" + end if + return + end if + + if (present(initial_capacity)) then + component_registry%capacity = max(10, initial_capacity) + end if + + if (present(verbose)) then + component_registry%verbose = verbose + end if + + ! Allocate array + allocate(component_registry%components(component_registry%capacity)) + + component_registry%initialized = .true. + component_registry%count = 0 + + if (component_registry%verbose) then + print *, "[INIT] Registry initialized, capacity:", component_registry%capacity + end if + end subroutine initialize_registry + + ! Cleanup registry + subroutine cleanup_registry + call component_registry%clear() + if (component_registry%verbose) then + print *, "[CLEANUP] Registry cleaned up" + end if + end subroutine cleanup_registry + + ! Simple registration (no factory) + subroutine register_component_simple(category, name) + character(len=*), intent(in) :: category, name + + type(component_info) :: info + + info%category = to_lower(trim(adjustl(category))) + info%name = to_lower(trim(adjustl(name))) + info%order = 0 + + call component_registry%register(info) + end subroutine register_component_simple + + ! Check if component exists + function has_component(category, name) result(found) + character(len=*), intent(in) :: category, name + logical :: found + + type(component_info) :: info + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + info = component_registry%get(cat_lower, name_lower) + found = (len_trim(info%category) > 0) + end function has_component + + ! Get available components in a category + subroutine get_available_components(category, names, orders) + character(len=*), intent(in) :: category + character(len=:), allocatable, intent(out), optional :: names(:) + integer, allocatable, intent(out), optional :: orders(:) + + character(len=32) :: cat_lower + integer :: i, count, idx + type(component_info) :: info + + cat_lower = to_lower(trim(adjustl(category))) + + ! Count components in this category + count = 0 + do i = 1, component_registry%count + if (component_registry%components(i)%category == cat_lower) then + count = count + 1 + end if + end do + + ! Allocate arrays if requested + if (present(names)) then + allocate(character(len=32) :: names(count)) + end if + + if (present(orders)) then + allocate(orders(count)) + end if + + ! Fill arrays + idx = 1 + do i = 1, component_registry%count + if (component_registry%components(i)%category == cat_lower) then + info = component_registry%components(i) + if (present(names)) then + names(idx) = info%name + end if + if (present(orders)) then + orders(idx) = info%order + end if + idx = idx + 1 + end if + end do + end subroutine get_available_components + + ! Public function to check if registry is initialized + function registry_is_initialized() result(is_initialized) + logical :: is_initialized + is_initialized = component_registry%is_initialized() + end function registry_is_initialized + + ! Public function to get registry size + function registry_get_size() result(size_val) + integer :: size_val + size_val = component_registry%size() + end function registry_get_size + + ! ==================== COMPONENT INFO METHODS ==================== + + subroutine ci_print(this) + class(component_info), intent(in) :: this + + if (this%order > 0) then + print *, " [", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")]" + else + print *, " [", trim(this%category), ".", trim(this%name), "]" + end if + end subroutine ci_print + + ! ==================== REGISTRY INTERNAL METHODS ==================== + + subroutine cr_register(this, info) + class(component_registry_type), intent(inout) :: this + type(component_info), intent(in) :: info + + type(component_info), allocatable :: temp(:) + integer :: i + + if (.not. this%initialized) then + error stop "[ERROR] Registry not initialized, call initialize_registry first" + end if + + ! Check if already exists + do i = 1, this%count + if (this%components(i)%category == info%category .and. & + this%components(i)%name == info%name) then + if (this%verbose) then + print *, "[WARN] Overwriting: ", & + trim(info%category), ".", trim(info%name) + end if + this%components(i) = info + return + end if + end do + + ! Expand array if needed + if (this%count >= this%capacity) then + this%capacity = this%capacity * 2 + allocate(temp(this%capacity)) + temp(1:this%count) = this%components(1:this%count) + call move_alloc(temp, this%components) + + if (this%verbose) then + print *, "[INFO] Registry expanded to capacity:", this%capacity + end if + end if + + ! Add component + this%count = this%count + 1 + this%components(this%count) = info + + if (this%verbose) then + print *, "[OK] Registered: ", trim(info%category), ".", trim(info%name) + end if + end subroutine cr_register + + function cr_get(this, category, name) result(info) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + type(component_info) :: info + + integer :: i + + ! Initialize return value as empty + info%category = "" + info%name = "" + info%order = 0 + + if (.not. this%initialized) then + return + end if + + do i = 1, this%count + if (this%components(i)%category == category .and. & + this%components(i)%name == name) then + info = this%components(i) + return + end if + end do + end function cr_get + + subroutine cr_list_all(this) + class(component_registry_type), intent(in) :: this + integer :: i + + if (.not. this%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + print *, "=== Registry Contents (", this%count, " components) ===" + + if (this%count == 0) then + print *, " (empty)" + return + end if + + ! Show components grouped by category + do i = 1, this%count + call this%components(i)%print() + end do + + print *, "===========================================" + end subroutine cr_list_all + + subroutine cr_clear(this) + class(component_registry_type), intent(inout) :: this + + if (allocated(this%components)) then + deallocate(this%components) + end if + + this%count = 0 + this%capacity = 100 + this%initialized = .false. + end subroutine cr_clear + + integer function cr_size(this) + class(component_registry_type), intent(in) :: this + cr_size = this%count + end function cr_size + + logical function cr_is_initialized(this) + class(component_registry_type), intent(in) :: this + cr_is_initialized = this%initialized + end function cr_is_initialized + + ! ==================== UTILITY FUNCTIONS ==================== + + function to_lower(str) result(lower_str) + character(len=*), intent(in) :: str + character(len=len(str)) :: lower_str + integer :: i + + do i = 1, len(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + lower_str(i:i) = char(ichar(str(i:i)) + 32) + else + lower_str(i:i) = str(i:i) + end if + end do + end function to_lower + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01f/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..2b72c548 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/src/infrastructure/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "配置基础设施模块...") + +add_library(infrastructure + config.f90 + mesh.f90 +) + +target_link_libraries(infrastructure PUBLIC core) + +target_include_directories(infrastructure PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS infrastructure + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "基础设施模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/01f/src/infrastructure/config.f90 new file mode 100644 index 00000000..34019f2f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/src/infrastructure/config.f90 @@ -0,0 +1,90 @@ +! src/infrastructure/config.f90 +module config_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, cfd_config, config_print, config_with_reconstruction + + ! CFD configuration type + type :: cfd_config + character(len=20) :: ic_type = "step" + character(len=20) :: recon_scheme = "eno" + character(len=20) :: flux_type = "rusanov" + integer :: rk_order = 1 + real(real64) :: wave_speed = 1.0_real64 + real(real64) :: final_time = 0.625_real64 + real(real64) :: dt = 0.025_real64 + character(len=20) :: boundary_type = "periodic" + real(real64) :: left_boundary_value = 1.0_real64 + real(real64) :: right_boundary_value = 2.0_real64 + integer :: spatial_order = 2 + logical :: verbose = .true. + end type cfd_config + +contains + + subroutine config_print(this) + type(cfd_config), intent(in) :: this + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(this%ic_type) + print *, "Reconstruction: ", trim(this%recon_scheme), " (order:", this%spatial_order, ")" + print *, "Flux type: ", trim(this%flux_type) + print *, "Time integration: RK", this%rk_order + print *, "Wave speed: ", this%wave_speed + print *, "Final time: ", this%final_time + print *, "Time step: ", this%dt + print *, "Boundary: ", trim(this%boundary_type) + if (trim(this%boundary_type) == 'dirichlet') then + print *, " Dirichlet values: [", this%left_boundary_value, ", ", & + this%right_boundary_value, "]" + end if + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(this, scheme, order) + type(cfd_config), intent(inout) :: this + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + character(len=20) :: scheme_lower + + ! Convert to lowercase + scheme_lower = scheme + call to_lower_inplace(scheme_lower) + this%recon_scheme = trim(adjustl(scheme_lower)) + + ! Set order + if (present(order)) then + this%spatial_order = order + else + ! Smart defaults + if (index(this%recon_scheme, 'weno') > 0) then + this%spatial_order = 5 + else if (trim(this%recon_scheme) == 'eno') then + this%spatial_order = 3 + else + print *, "[ERROR] Unsupported reconstruction scheme: ", trim(this%recon_scheme) + return + end if + end if + + if (this%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(this%recon_scheme), & + " Order: ", this%spatial_order + end if + end subroutine config_with_reconstruction + + subroutine to_lower_inplace(str) + character(len=*), intent(inout) :: str + integer :: i + + do i = 1, len_trim(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + str(i:i) = char(ichar(str(i:i)) + 32) + end if + end do + end subroutine to_lower_inplace + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/01f/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..65a45dcd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/src/infrastructure/mesh.f90 @@ -0,0 +1,74 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, mesh_type, mesh_init, mesh_print_info + + ! 网格类型 + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer :: ncells = 40 + integer :: nnodes + integer :: nx + real(wp), allocatable :: x(:) ! 节点坐标 + real(wp), allocatable :: xcc(:) ! 单元中心坐标 + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer, optional, intent(in) :: ncells + + integer :: i + + ! 设置参数 + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! 计算派生参数 + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! 分配数组 + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! 生成节点坐标 + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! 生成单元中心坐标 + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== 网格信息 ===" + print *, "计算域: [", this%xmin, ", ", this%xmax, "]" + print *, "单元数: ", this%ncells + print *, "节点数: ", this%nnodes + print *, "网格尺寸 dx: ", this%dx + print *, "域长度 L: ", this%L + print *, "==========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01f/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01f/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..b2617f29 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux + base.f90 +) + +target_link_libraries(flux PUBLIC core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/01f/src/numerics/flux/base.f90 new file mode 100644 index 00000000..d9bd640c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/src/numerics/flux/base.f90 @@ -0,0 +1,24 @@ +! src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/01f/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..414f783d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/src/numerics/flux/rusanov.f90 @@ -0,0 +1,25 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + +contains + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + call flux_info(this) + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01f/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..77808c1a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor + base.f90 +) + +target_link_libraries(reconstructor PUBLIC core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/01f/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..8d3119ee --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/src/numerics/reconstructor/base.f90 @@ -0,0 +1,27 @@ +! src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/01f/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..19029b6d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,25 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + +contains + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + call reconstructor_info(this) + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/01f/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..4f268ac9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,25 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno3_reconstructor + + type, extends(reconstructor_base) :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + +contains + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + call reconstructor_info(this) + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01f/tests/CMakeLists.txt new file mode 100644 index 00000000..a9858189 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/tests/CMakeLists.txt @@ -0,0 +1,16 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +# Simple functionality test +add_executable(test_simple + test_minimal_simple.f90 +) +target_link_libraries(test_simple + core + infrastructure +) +target_include_directories(test_simple PRIVATE + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Tests configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/tests/test_factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/01f/tests/test_factory.f90 new file mode 100644 index 00000000..8968b9c2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/tests/test_factory.f90 @@ -0,0 +1,154 @@ +! tests/test_factory.f90 +program test_factory + use registry_module + use config_module + use mesh_module + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + class(*), allocatable :: instance + class(reconstructor_base), allocatable :: recon + class(flux_calculator_base), allocatable :: flux_calc + real(wp), allocatable :: q(:), qL(:), qR(:), flux(:) + integer :: i, n + + print *, "=== Factory Pattern Test ===" + print *, "" + + ! Initialize systems + call initialize_registry(verbose=.true.) + + ! Register components with factories + print *, "1. Registering components with factories..." + call register_component_with_factory("reconstructor", "eno", create_eno, 3) + call register_component_with_factory("reconstructor", "weno3", create_weno3, 3) + call register_component_with_factory("flux", "rusanov", create_rusanov) + + call component_registry%list_all() + print *, "" + + ! Test creating ENO reconstructor + print *, "2. Creating ENO reconstructor..." + call create_component("reconstructor", "eno", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (eno_reconstructor) + allocate(recon, source=inst) + print *, "ENO reconstructor created successfully" + call recon%info() + print *, "" + + ! Test reconstruction + n = 10 + allocate(q(0:n+1), qL(n), qR(n)) + + ! Initialize test data (sine wave) + do i = 0, n+1 + q(i) = sin(2.0_wp * 3.141592653589793_wp * real(i-1, wp) / real(n, wp)) + end do + + print *, "Testing ENO reconstruction..." + call recon%reconstruct(q, qL, qR) + + print *, "q (internal):" + do i = 1, n + write(*, '(I3, F10.6)') i, q(i) + end do + + print *, "qL (left interface values):" + do i = 1, n + write(*, '(I3, F10.6)') i, qL(i) + end do + + deallocate(q, qL, qR) + class default + print *, "[ERROR] Wrong type for ENO reconstructor" + end select + else + print *, "[ERROR] Failed to create ENO reconstructor" + end if + print *, "" + + ! Test creating WENO3 reconstructor + print *, "3. Creating WENO3 reconstructor..." + if (allocated(instance)) deallocate(instance) + call create_component("reconstructor", "weno3", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (weno3_reconstructor) + if (allocated(recon)) deallocate(recon) + allocate(recon, source=inst) + print *, "WENO3 reconstructor created successfully" + call recon%info() + class default + print *, "[ERROR] Wrong type for WENO3 reconstructor" + end select + else + print *, "[ERROR] Failed to create WENO3 reconstructor" + end if + print *, "" + + ! Test creating Rusanov flux calculator + print *, "4. Creating Rusanov flux calculator..." + if (allocated(instance)) deallocate(instance) + call create_component("flux", "rusanov", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (rusanov_flux) + allocate(flux_calc, source=inst) + print *, "Rusanov flux calculator created successfully" + call flux_calc%info() + print *, "" + + ! Test flux computation + n = 5 + allocate(qL(n), qR(n), flux(n)) + + ! Initialize test data + do i = 1, n + qL(i) = 1.0_wp + 0.1_wp * real(i-1, wp) + qR(i) = 1.0_wp + 0.1_wp * real(i, wp) + end do + + print *, "Testing Rusanov flux computation..." + call flux_calc%compute(qL, qR, flux, 1.0_wp) + + print *, "qL:" + do i = 1, n + write(*, '(I3, F10.6)') i, qL(i) + end do + + print *, "qR:" + do i = 1, n + write(*, '(I3, F10.6)') i, qR(i) + end do + + print *, "Flux:" + do i = 1, n + write(*, '(I3, F10.6)') i, flux(i) + end do + + deallocate(qL, qR, flux) + class default + print *, "[ERROR] Wrong type for Rusanov flux" + end select + else + print *, "[ERROR] Failed to create Rusanov flux calculator" + end if + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Factory pattern test completed successfully ===" + +end program test_factory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/tests/test_minimal.f90 b/example/1d-linear-convection/weno3/fortran/registry/01f/tests/test_minimal.f90 new file mode 100644 index 00000000..bb4b7cda --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/tests/test_minimal.f90 @@ -0,0 +1,108 @@ +! tests/test_minimal.f90 +program test_minimal + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i ! Declare loop variable here + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", component_registry%size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components + print *, "5. Testing available components" + print *, "--------------------------------" + + ! Use a separate block to avoid allocation issues + call test_available_components() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Minimal test completed successfully ===" + +contains + + ! Internal subroutine for testing available components + subroutine test_available_components() + character(len=:), allocatable :: names(:) + integer, allocatable :: orders(:) + integer :: j + + call get_available_components("reconstructor", names, orders) + + if (allocated(names)) then + print *, "Reconstructors:" + do j = 1, size(names) + print *, " - ", trim(names(j)) + if (allocated(orders)) then + print *, " Order: ", orders(j) + end if + end do + else + print *, "No reconstructors found" + end if + end subroutine test_available_components + +end program test_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01f/tests/test_minimal_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/01f/tests/test_minimal_simple.f90 new file mode 100644 index 00000000..689165de --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01f/tests/test_minimal_simple.f90 @@ -0,0 +1,84 @@ +! tests/test_minimal_simple.f90 +program test_minimal_simple + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Simple Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components (simple version) + print *, "5. Testing registry functionality" + print *, "---------------------------------" + print *, "Registry initialized: ", registry_is_initialized() + print *, "Number of components: ", registry_get_size() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Simple test completed successfully ===" + +end program test_minimal_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01g/CMakeLists.txt new file mode 100644 index 00000000..ef66d584 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/README.md b/example/1d-linear-convection/weno3/fortran/registry/01g/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/01g/scripts/build.bat new file mode 100644 index 00000000..d243695b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/scripts/build.bat @@ -0,0 +1,26 @@ +@echo off +echo ======================================== +echo Fortran CFD Project Builder +echo (Wrapper for Python script) +echo ======================================== +echo. + +REM 检查Python +where python >nul 2>nul +if %errorlevel% neq 0 ( + echo [ERROR] Python not found. Please install Python 3. + pause + exit /b 1 +) + +REM 运行Python构建脚本 +echo [INFO] Running Python build script... +python build.py %* + +if %errorlevel% neq 0 ( + echo [ERROR] Build failed + pause + exit /b 1 +) + +pause \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/scripts/build.ps1 b/example/1d-linear-convection/weno3/fortran/registry/01g/scripts/build.ps1 new file mode 100644 index 00000000..4497c5eb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/scripts/build.ps1 @@ -0,0 +1,32 @@ +# Fortran CFD Project Builder (PowerShell wrapper) + +Write-Host "========================================" -ForegroundColor Cyan +Write-Host " Fortran CFD Project Builder" -ForegroundColor Cyan +Write-Host " (PowerShell wrapper for Python script)" -ForegroundColor Cyan +Write-Host "========================================" -ForegroundColor Cyan +Write-Host "" + +# 检查Python +$python = Get-Command python -ErrorAction SilentlyContinue +if (-not $python) { + $python = Get-Command python3 -ErrorAction SilentlyContinue +} + +if (-not $python) { + Write-Host "[ERROR] Python not found. Please install Python 3." -ForegroundColor Red + Read-Host "Press Enter to exit" + exit 1 +} + +# 运行Python构建脚本 +Write-Host "[INFO] Running Python build script..." -ForegroundColor Yellow +$argsString = $args -join ' ' +$command = "python build.py $argsString" + +Invoke-Expression $command + +if ($LASTEXITCODE -ne 0) { + Write-Host "[ERROR] Build failed" -ForegroundColor Red + Read-Host "Press Enter to exit" + exit 1 +} \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/01g/scripts/build.py new file mode 100644 index 00000000..1c99c2c8 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/scripts/build.py @@ -0,0 +1,357 @@ +#!/usr/bin/env python3 +""" +Fortran CFD 项目构建脚本 (Python版) +支持 Intel oneAPI 环境的自动化构建 +""" + +import os +import sys +import subprocess +import shutil +from pathlib import Path +import argparse +import platform +import time + +class Colors: + """终端颜色""" + if platform.system() == "Windows": + # Windows 启用 ANSI + os.system("") + + HEADER = '\033[95m' + BLUE = '\033[94m' + CYAN = '\033[96m' + GREEN = '\033[92m' + YELLOW = '\033[93m' + RED = '\033[91m' + MAGENTA = '\033[95m' + DARK_GRAY = '\033[90m' # 添加这个 + END = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + +def print_color(text, color=Colors.END): + """彩色打印""" + print(f"{color}{text}{Colors.END}") + +def print_header(text): + """打印标题""" + print_color("\n" + "="*60, Colors.CYAN) + print_color(f" {text}", Colors.BOLD + Colors.CYAN) + print_color("="*60 + "\n", Colors.CYAN) + +def print_step(step, total, message): + """打印步骤""" + print_color(f"[{step}/{total}] {message}...", Colors.YELLOW) + +def print_success(message): + """打印成功""" + print_color(f"✓ {message}", Colors.GREEN) + +def print_error(message): + """打印错误""" + print_color(f"✗ {message}", Colors.RED) + +def print_warning(message): + """打印警告""" + print_color(f"! {message}", Colors.YELLOW) + +def run_command(cmd, cwd=None, check=True, capture=True): + """运行命令""" + print_color(f" $ {' '.join(cmd)}", Colors.BLUE) + + try: + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=capture, + text=True, + encoding='utf-8', + errors='ignore', + shell=False + ) + + if capture and result.stdout: + print(result.stdout) + if capture and result.stderr: + print_color(result.stderr, Colors.YELLOW) + + if check and result.returncode != 0: + print_error(f"Command failed with exit code: {result.returncode}") + return False + + return True + + except FileNotFoundError as e: + print_error(f"Command not found: {cmd[0]}") + if check: + raise + return False + except Exception as e: + print_error(f"Command execution failed: {e}") + return False + +def setup_intel_environment(): + """设置 Intel oneAPI 环境""" + setvars_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + ] + + setvars_path = None + for path in setvars_paths: + if os.path.exists(path): + setvars_path = path + break + + if not setvars_path: + print_error("Intel oneAPI setvars.bat not found.") + print_warning("Please install Intel oneAPI or update the path in build.py") + return False + + # 创建临时的 batch 文件 + temp_bat = "temp_setvars.bat" + with open(temp_bat, 'w') as f: + f.write(f'@echo off\n') + f.write(f'call "{setvars_path}" > nul 2>&1\n') + f.write(f'set\n') + + try: + # 运行并捕获环境变量 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + shell=True, + encoding='utf-8', + errors='ignore' + ) + + # 解析并设置环境变量 + for line in result.stdout.split('\n'): + if '=' in line: + key, value = line.split('=', 1) + os.environ[key.strip()] = value.strip() + + os.remove(temp_bat) + print_success("Intel oneAPI environment configured") + return True + + except Exception as e: + print_error(f"Failed to setup Intel environment: {e}") + if os.path.exists(temp_bat): + os.remove(temp_bat) + return False + +def build_project(args): + """构建项目主函数""" + start_time = time.time() + + print_header(f"Fortran CFD Project Builder") + print_color(f"Build type: {args.build_type}", Colors.CYAN) + print_color(f"Run tests: {args.run_tests}", Colors.CYAN) + print() + + # 获取项目根目录(脚本所在目录的父目录) + script_dir = Path(__file__).parent + project_root = script_dir.parent + os.chdir(project_root) + + print_color(f"Project root: {project_root}", Colors.BLUE) + + # 步骤1: 设置 Intel 环境 + print_step(1, 4, "Setting up Intel Fortran compiler") + if not setup_intel_environment(): + return False + + # 步骤2: 准备构建目录 + print_step(2, 4, "Preparing build directory") + build_dir = project_root / "build" + + if args.clean and build_dir.exists(): + try: + shutil.rmtree(build_dir) + print_success("Cleaned build directory") + except Exception as e: + print_error(f"Failed to clean build directory: {e}") + if not args.force: + return False + + build_dir.mkdir(exist_ok=True) + os.chdir(build_dir) + + # 步骤3: 配置项目 + print_step(3, 4, "Configuring project") + + cmake_cmd = [ + "cmake", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-T", "fortran=ifx", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ".." + ] + + if not run_command(cmake_cmd, check=not args.force): + if not args.force: + return False + + # 步骤4: 构建项目 + print_step(4, 4, "Building project") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + f"-j{args.jobs}" if args.jobs > 1 else "" + ] + + print(f"build_cmd={build_cmd}") + build_cmd = [c for c in build_cmd if c] # 移除空字符串 + + if not run_command(build_cmd, check=not args.force): + if not args.force: + return False + + build_time = time.time() - start_time + print_success(f"Build completed in {build_time:.1f} seconds") + + # 运行测试 + if args.run_tests: + print_header("Running Tests") + run_tests(args.build_type) + + print_header("Build Summary") + print_color(f"Build directory: {build_dir}", Colors.GREEN) + print_color(f"Build type: {args.build_type}", Colors.GREEN) + print_color(f"Total time: {build_time:.1f}s", Colors.GREEN) + + return True + +def run_tests(build_type): + """运行测试""" + tests = [ + ("test_simple", "Simple functionality test"), + ("test_factory", "Factory pattern test"), + ] + + for test_name, description in tests: + # 修复:在这里直接访问当前循环的 test_name + _run_single_test(test_name, description, build_type) + +def _run_single_test(test_name, description, build_type): + """运行单个测试(修复作用域问题)""" + # 可能的测试可执行文件路径 + possible_paths = [ + Path(f"./{build_type}/{test_name}.exe"), + Path(f"./tests/{build_type}/{test_name}.exe"), + Path(f"./tests/{test_name}.exe"), + Path(f"{test_name}.exe"), + Path(f"./{test_name}.exe"), + ] + + test_exe = None + + # 尝试所有可能的路径 + for path in possible_paths: + if path.exists(): + test_exe = path + break + + if test_exe: + print_color(f"\n[TEST] {description}...", Colors.MAGENTA) + print_color("-" * 50, Colors.DARK_GRAY) + + try: + result = subprocess.run( + [str(test_exe)], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + + if result.stdout: + print(result.stdout) + + if result.returncode == 0: + print_success(f"{test_name} passed") + else: + print_error(f"{test_name} failed (exit code: {result.returncode})") + if result.stderr: + print_color(result.stderr, Colors.YELLOW) + + except Exception as e: + print_error(f"{test_name} failed: {e}") + else: + print_warning(f"{test_name}.exe not found. Searched paths:") + for path in possible_paths: + print_color(f" - {path}", Colors.YELLOW) + +def main(): + """主函数""" + parser = argparse.ArgumentParser(description="Fortran CFD Project Builder") + + parser.add_argument( + "--build-type", + choices=["Debug", "Release", "RelWithDebInfo", "MinSizeRel"], + default="Debug", + help="Build type (default: Debug)" + ) + + parser.add_argument( + "--clean", + action="store_true", + help="Clean build directory before building" + ) + + parser.add_argument( + "--no-tests", + action="store_true", + help="Skip running tests" + ) + + parser.add_argument( + "-j", "--jobs", + type=int, + default=0, + help="Number of parallel jobs (0 = use all cores)" + ) + + parser.add_argument( + "--force", + action="store_true", + help="Continue on errors" + ) + + parser.add_argument( + "--verbose", + action="store_true", + help="Verbose output" + ) + + args = parser.parse_args() + + if args.jobs == 0: + import multiprocessing + args.jobs = multiprocessing.cpu_count() + + args.run_tests = not args.no_tests + + try: + success = build_project(args) + if not success and not args.force: + sys.exit(1) + except KeyboardInterrupt: + print_color("\nBuild interrupted by user", Colors.YELLOW) + sys.exit(1) + except Exception as e: + print_error(f"Unexpected error: {e}") + if args.verbose: + import traceback + traceback.print_exc() + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/01g/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01g/src/CMakeLists.txt new file mode 100644 index 00000000..ee38952b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/src/CMakeLists.txt @@ -0,0 +1,14 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01g/src/core/CMakeLists.txt new file mode 100644 index 00000000..376dd4fe --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/src/core/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/core/CMakeLists.txt +message(STATUS "配置核心模块...") + +add_library(core + registry.f90 + factory_interfaces.f90 +) + +target_include_directories(core PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 安装规则 +install(TARGETS core + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) +install(FILES + ${CMAKE_Fortran_MODULE_DIRECTORY}/registry_module.mod + ${CMAKE_Fortran_MODULE_DIRECTORY}/factory_interfaces.mod + DESTINATION include/fortran_cfd/core +) + +message(STATUS "核心模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/01g/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..19cc2c59 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/01g/src/core/registry.f90 new file mode 100644 index 00000000..f996c89b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/src/core/registry.f90 @@ -0,0 +1,318 @@ +! src/core/registry.f90 +module registry_module + use, intrinsic :: iso_fortran_env, only: real64 + use factory_interfaces, only: factory_procedure + implicit none + + private + + ! Public interface + public :: real64, component_info, component_registry + public :: register_component_simple, initialize_registry, cleanup_registry + public :: has_component, get_available_components + public :: registry_is_initialized, registry_get_size ! 添加公共访问方法 + + ! Type definitions (simplified, no factory for now) + type :: component_info + character(len=32) :: category = "" + character(len=32) :: name = "" + integer :: order = 0 + contains + procedure :: print => ci_print + end type component_info + + type :: component_registry_type + private + type(component_info), allocatable :: components(:) + integer :: count = 0 + integer :: capacity = 100 + logical :: verbose = .true. + logical :: initialized = .false. + contains + procedure :: register => cr_register + procedure :: get => cr_get + procedure :: list_all => cr_list_all + procedure :: clear => cr_clear + procedure :: size => cr_size + procedure :: is_initialized => cr_is_initialized ! 内部方法 + end type component_registry_type + + ! Global registry instance + type(component_registry_type), save :: component_registry + +contains + + ! ==================== PUBLIC API ==================== + + ! Initialize registry + subroutine initialize_registry(initial_capacity, verbose) + integer, optional, intent(in) :: initial_capacity + logical, optional, intent(in) :: verbose + + if (component_registry%initialized) then + if (component_registry%verbose) then + print *, "[INFO] Registry already initialized" + end if + return + end if + + if (present(initial_capacity)) then + component_registry%capacity = max(10, initial_capacity) + end if + + if (present(verbose)) then + component_registry%verbose = verbose + end if + + ! Allocate array + allocate(component_registry%components(component_registry%capacity)) + + component_registry%initialized = .true. + component_registry%count = 0 + + if (component_registry%verbose) then + print *, "[INIT] Registry initialized, capacity:", component_registry%capacity + end if + end subroutine initialize_registry + + ! Cleanup registry + subroutine cleanup_registry + call component_registry%clear() + if (component_registry%verbose) then + print *, "[CLEANUP] Registry cleaned up" + end if + end subroutine cleanup_registry + + ! Simple registration (no factory) + subroutine register_component_simple(category, name) + character(len=*), intent(in) :: category, name + + type(component_info) :: info + + info%category = to_lower(trim(adjustl(category))) + info%name = to_lower(trim(adjustl(name))) + info%order = 0 + + call component_registry%register(info) + end subroutine register_component_simple + + ! Check if component exists + function has_component(category, name) result(found) + character(len=*), intent(in) :: category, name + logical :: found + + type(component_info) :: info + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + info = component_registry%get(cat_lower, name_lower) + found = (len_trim(info%category) > 0) + end function has_component + + ! Get available components in a category + subroutine get_available_components(category, names, orders) + character(len=*), intent(in) :: category + character(len=:), allocatable, intent(out), optional :: names(:) + integer, allocatable, intent(out), optional :: orders(:) + + character(len=32) :: cat_lower + integer :: i, count, idx + type(component_info) :: info + + cat_lower = to_lower(trim(adjustl(category))) + + ! Count components in this category + count = 0 + do i = 1, component_registry%count + if (component_registry%components(i)%category == cat_lower) then + count = count + 1 + end if + end do + + ! Allocate arrays if requested + if (present(names)) then + allocate(character(len=32) :: names(count)) + end if + + if (present(orders)) then + allocate(orders(count)) + end if + + ! Fill arrays + idx = 1 + do i = 1, component_registry%count + if (component_registry%components(i)%category == cat_lower) then + info = component_registry%components(i) + if (present(names)) then + names(idx) = info%name + end if + if (present(orders)) then + orders(idx) = info%order + end if + idx = idx + 1 + end if + end do + end subroutine get_available_components + + ! Public function to check if registry is initialized + function registry_is_initialized() result(is_initialized) + logical :: is_initialized + is_initialized = component_registry%is_initialized() + end function registry_is_initialized + + ! Public function to get registry size + function registry_get_size() result(size_val) + integer :: size_val + size_val = component_registry%size() + end function registry_get_size + + ! ==================== COMPONENT INFO METHODS ==================== + + subroutine ci_print(this) + class(component_info), intent(in) :: this + + if (this%order > 0) then + print *, " [", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")]" + else + print *, " [", trim(this%category), ".", trim(this%name), "]" + end if + end subroutine ci_print + + ! ==================== REGISTRY INTERNAL METHODS ==================== + + subroutine cr_register(this, info) + class(component_registry_type), intent(inout) :: this + type(component_info), intent(in) :: info + + type(component_info), allocatable :: temp(:) + integer :: i + + if (.not. this%initialized) then + error stop "[ERROR] Registry not initialized, call initialize_registry first" + end if + + ! Check if already exists + do i = 1, this%count + if (this%components(i)%category == info%category .and. & + this%components(i)%name == info%name) then + if (this%verbose) then + print *, "[WARN] Overwriting: ", & + trim(info%category), ".", trim(info%name) + end if + this%components(i) = info + return + end if + end do + + ! Expand array if needed + if (this%count >= this%capacity) then + this%capacity = this%capacity * 2 + allocate(temp(this%capacity)) + temp(1:this%count) = this%components(1:this%count) + call move_alloc(temp, this%components) + + if (this%verbose) then + print *, "[INFO] Registry expanded to capacity:", this%capacity + end if + end if + + ! Add component + this%count = this%count + 1 + this%components(this%count) = info + + if (this%verbose) then + print *, "[OK] Registered: ", trim(info%category), ".", trim(info%name) + end if + end subroutine cr_register + + function cr_get(this, category, name) result(info) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + type(component_info) :: info + + integer :: i + + ! Initialize return value as empty + info%category = "" + info%name = "" + info%order = 0 + + if (.not. this%initialized) then + return + end if + + do i = 1, this%count + if (this%components(i)%category == category .and. & + this%components(i)%name == name) then + info = this%components(i) + return + end if + end do + end function cr_get + + subroutine cr_list_all(this) + class(component_registry_type), intent(in) :: this + integer :: i + + if (.not. this%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + print *, "=== Registry Contents (", this%count, " components) ===" + + if (this%count == 0) then + print *, " (empty)" + return + end if + + ! Show components grouped by category + do i = 1, this%count + call this%components(i)%print() + end do + + print *, "===========================================" + end subroutine cr_list_all + + subroutine cr_clear(this) + class(component_registry_type), intent(inout) :: this + + if (allocated(this%components)) then + deallocate(this%components) + end if + + this%count = 0 + this%capacity = 100 + this%initialized = .false. + end subroutine cr_clear + + integer function cr_size(this) + class(component_registry_type), intent(in) :: this + cr_size = this%count + end function cr_size + + logical function cr_is_initialized(this) + class(component_registry_type), intent(in) :: this + cr_is_initialized = this%initialized + end function cr_is_initialized + + ! ==================== UTILITY FUNCTIONS ==================== + + function to_lower(str) result(lower_str) + character(len=*), intent(in) :: str + character(len=len(str)) :: lower_str + integer :: i + + do i = 1, len(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + lower_str(i:i) = char(ichar(str(i:i)) + 32) + else + lower_str(i:i) = str(i:i) + end if + end do + end function to_lower + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01g/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..2b72c548 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/src/infrastructure/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "配置基础设施模块...") + +add_library(infrastructure + config.f90 + mesh.f90 +) + +target_link_libraries(infrastructure PUBLIC core) + +target_include_directories(infrastructure PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS infrastructure + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "基础设施模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/01g/src/infrastructure/config.f90 new file mode 100644 index 00000000..34019f2f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/src/infrastructure/config.f90 @@ -0,0 +1,90 @@ +! src/infrastructure/config.f90 +module config_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, cfd_config, config_print, config_with_reconstruction + + ! CFD configuration type + type :: cfd_config + character(len=20) :: ic_type = "step" + character(len=20) :: recon_scheme = "eno" + character(len=20) :: flux_type = "rusanov" + integer :: rk_order = 1 + real(real64) :: wave_speed = 1.0_real64 + real(real64) :: final_time = 0.625_real64 + real(real64) :: dt = 0.025_real64 + character(len=20) :: boundary_type = "periodic" + real(real64) :: left_boundary_value = 1.0_real64 + real(real64) :: right_boundary_value = 2.0_real64 + integer :: spatial_order = 2 + logical :: verbose = .true. + end type cfd_config + +contains + + subroutine config_print(this) + type(cfd_config), intent(in) :: this + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(this%ic_type) + print *, "Reconstruction: ", trim(this%recon_scheme), " (order:", this%spatial_order, ")" + print *, "Flux type: ", trim(this%flux_type) + print *, "Time integration: RK", this%rk_order + print *, "Wave speed: ", this%wave_speed + print *, "Final time: ", this%final_time + print *, "Time step: ", this%dt + print *, "Boundary: ", trim(this%boundary_type) + if (trim(this%boundary_type) == 'dirichlet') then + print *, " Dirichlet values: [", this%left_boundary_value, ", ", & + this%right_boundary_value, "]" + end if + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(this, scheme, order) + type(cfd_config), intent(inout) :: this + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + character(len=20) :: scheme_lower + + ! Convert to lowercase + scheme_lower = scheme + call to_lower_inplace(scheme_lower) + this%recon_scheme = trim(adjustl(scheme_lower)) + + ! Set order + if (present(order)) then + this%spatial_order = order + else + ! Smart defaults + if (index(this%recon_scheme, 'weno') > 0) then + this%spatial_order = 5 + else if (trim(this%recon_scheme) == 'eno') then + this%spatial_order = 3 + else + print *, "[ERROR] Unsupported reconstruction scheme: ", trim(this%recon_scheme) + return + end if + end if + + if (this%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(this%recon_scheme), & + " Order: ", this%spatial_order + end if + end subroutine config_with_reconstruction + + subroutine to_lower_inplace(str) + character(len=*), intent(inout) :: str + integer :: i + + do i = 1, len_trim(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + str(i:i) = char(ichar(str(i:i)) + 32) + end if + end do + end subroutine to_lower_inplace + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/01g/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..65a45dcd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/src/infrastructure/mesh.f90 @@ -0,0 +1,74 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, mesh_type, mesh_init, mesh_print_info + + ! 网格类型 + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer :: ncells = 40 + integer :: nnodes + integer :: nx + real(wp), allocatable :: x(:) ! 节点坐标 + real(wp), allocatable :: xcc(:) ! 单元中心坐标 + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer, optional, intent(in) :: ncells + + integer :: i + + ! 设置参数 + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! 计算派生参数 + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! 分配数组 + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! 生成节点坐标 + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! 生成单元中心坐标 + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== 网格信息 ===" + print *, "计算域: [", this%xmin, ", ", this%xmax, "]" + print *, "单元数: ", this%ncells + print *, "节点数: ", this%nnodes + print *, "网格尺寸 dx: ", this%dx + print *, "域长度 L: ", this%L + print *, "==========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01g/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01g/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..b2617f29 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux + base.f90 +) + +target_link_libraries(flux PUBLIC core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/01g/src/numerics/flux/base.f90 new file mode 100644 index 00000000..d9bd640c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/src/numerics/flux/base.f90 @@ -0,0 +1,24 @@ +! src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/01g/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..4c1054ab --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/src/numerics/flux/rusanov.f90 @@ -0,0 +1,48 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + + ! 添加构造函数接口 + interface rusanov_flux + module procedure create_rusanov_flux + end interface + +contains + + ! 构造函数 + type(rusanov_flux) function create_rusanov_flux(name, wave_speed_default) result(this) + character(len=*), optional, intent(in) :: name + real(real64), optional, intent(in) :: wave_speed_default + + if (present(name)) then + this%name = name + else + this%name = "Rusanov" + end if + + if (present(wave_speed_default)) then + this%wave_speed_default = wave_speed_default + else + this%wave_speed_default = 1.0_real64 + end if + end function create_rusanov_flux + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + call flux_info(this) + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01g/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..77808c1a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor + base.f90 +) + +target_link_libraries(reconstructor PUBLIC core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/01g/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..404535d1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/src/numerics/reconstructor/base.f90 @@ -0,0 +1,40 @@ +! src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + procedure :: reconstruct => reconstruct_default ! 添加这个方法 + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + + ! 默认的重构方法 + subroutine reconstruct_default(this, q, qL, qR) + class(reconstructor_base), intent(in) :: this + real(real64), intent(in) :: q(0:) ! 包含ghost cells + real(real64), intent(out) :: qL(:), qR(:) + integer :: i, n + + n = size(qL) + do i = 1, n + qL(i) = q(i) ! 简单的一阶重构 + qR(i) = q(i+1) + end do + end subroutine reconstruct_default +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/01g/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..8bc206ac --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,56 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + + ! 添加构造函数接口 + interface eno_reconstructor + module procedure create_eno_reconstructor + end interface + +contains + + ! 构造函数 + type(eno_reconstructor) function create_eno_reconstructor(name, order, epsilon) result(this) + character(len=*), optional, intent(in) :: name + integer, optional, intent(in) :: order + real(real64), optional, intent(in) :: epsilon + + ! 设置默认值 + if (present(name)) then + this%name = name + else + this%name = "ENO" + end if + + if (present(order)) then + this%order = order + else + this%order = 3 + end if + + if (present(epsilon)) then + this%epsilon = epsilon + else + this%epsilon = 1.0e-6_real64 + end if + end function create_eno_reconstructor + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + call reconstructor_info(this) + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/01g/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..ffd50016 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,55 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno3_reconstructor + + type, extends(reconstructor_base) :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + + ! 添加构造函数接口 + interface weno3_reconstructor + module procedure create_weno3_reconstructor + end interface + +contains + + ! 构造函数 + type(weno3_reconstructor) function create_weno3_reconstructor(name, order, epsilon) result(this) + character(len=*), optional, intent(in) :: name + integer, optional, intent(in) :: order + real(real64), optional, intent(in) :: epsilon + + if (present(name)) then + this%name = name + else + this%name = "WENO3" + end if + + if (present(order)) then + this%order = order + else + this%order = 3 + end if + + if (present(epsilon)) then + this%epsilon = epsilon + else + this%epsilon = 1.0e-6_real64 + end if + end function create_weno3_reconstructor + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + call reconstructor_info(this) + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01g/tests/CMakeLists.txt new file mode 100644 index 00000000..d469614d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/tests/CMakeLists.txt @@ -0,0 +1,4 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +add_executable(test_minimal_simple test_minimal_simple.f90) diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/tests/test_factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/01g/tests/test_factory.f90 new file mode 100644 index 00000000..8968b9c2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/tests/test_factory.f90 @@ -0,0 +1,154 @@ +! tests/test_factory.f90 +program test_factory + use registry_module + use config_module + use mesh_module + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + class(*), allocatable :: instance + class(reconstructor_base), allocatable :: recon + class(flux_calculator_base), allocatable :: flux_calc + real(wp), allocatable :: q(:), qL(:), qR(:), flux(:) + integer :: i, n + + print *, "=== Factory Pattern Test ===" + print *, "" + + ! Initialize systems + call initialize_registry(verbose=.true.) + + ! Register components with factories + print *, "1. Registering components with factories..." + call register_component_with_factory("reconstructor", "eno", create_eno, 3) + call register_component_with_factory("reconstructor", "weno3", create_weno3, 3) + call register_component_with_factory("flux", "rusanov", create_rusanov) + + call component_registry%list_all() + print *, "" + + ! Test creating ENO reconstructor + print *, "2. Creating ENO reconstructor..." + call create_component("reconstructor", "eno", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (eno_reconstructor) + allocate(recon, source=inst) + print *, "ENO reconstructor created successfully" + call recon%info() + print *, "" + + ! Test reconstruction + n = 10 + allocate(q(0:n+1), qL(n), qR(n)) + + ! Initialize test data (sine wave) + do i = 0, n+1 + q(i) = sin(2.0_wp * 3.141592653589793_wp * real(i-1, wp) / real(n, wp)) + end do + + print *, "Testing ENO reconstruction..." + call recon%reconstruct(q, qL, qR) + + print *, "q (internal):" + do i = 1, n + write(*, '(I3, F10.6)') i, q(i) + end do + + print *, "qL (left interface values):" + do i = 1, n + write(*, '(I3, F10.6)') i, qL(i) + end do + + deallocate(q, qL, qR) + class default + print *, "[ERROR] Wrong type for ENO reconstructor" + end select + else + print *, "[ERROR] Failed to create ENO reconstructor" + end if + print *, "" + + ! Test creating WENO3 reconstructor + print *, "3. Creating WENO3 reconstructor..." + if (allocated(instance)) deallocate(instance) + call create_component("reconstructor", "weno3", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (weno3_reconstructor) + if (allocated(recon)) deallocate(recon) + allocate(recon, source=inst) + print *, "WENO3 reconstructor created successfully" + call recon%info() + class default + print *, "[ERROR] Wrong type for WENO3 reconstructor" + end select + else + print *, "[ERROR] Failed to create WENO3 reconstructor" + end if + print *, "" + + ! Test creating Rusanov flux calculator + print *, "4. Creating Rusanov flux calculator..." + if (allocated(instance)) deallocate(instance) + call create_component("flux", "rusanov", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (rusanov_flux) + allocate(flux_calc, source=inst) + print *, "Rusanov flux calculator created successfully" + call flux_calc%info() + print *, "" + + ! Test flux computation + n = 5 + allocate(qL(n), qR(n), flux(n)) + + ! Initialize test data + do i = 1, n + qL(i) = 1.0_wp + 0.1_wp * real(i-1, wp) + qR(i) = 1.0_wp + 0.1_wp * real(i, wp) + end do + + print *, "Testing Rusanov flux computation..." + call flux_calc%compute(qL, qR, flux, 1.0_wp) + + print *, "qL:" + do i = 1, n + write(*, '(I3, F10.6)') i, qL(i) + end do + + print *, "qR:" + do i = 1, n + write(*, '(I3, F10.6)') i, qR(i) + end do + + print *, "Flux:" + do i = 1, n + write(*, '(I3, F10.6)') i, flux(i) + end do + + deallocate(qL, qR, flux) + class default + print *, "[ERROR] Wrong type for Rusanov flux" + end select + else + print *, "[ERROR] Failed to create Rusanov flux calculator" + end if + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Factory pattern test completed successfully ===" + +end program test_factory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/tests/test_factory_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/01g/tests/test_factory_simple.f90 new file mode 100644 index 00000000..c0dcde27 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/tests/test_factory_simple.f90 @@ -0,0 +1,106 @@ +! tests/test_factory_simple.f90 +program test_factory_simple + use registry_module + use config_module + use mesh_module + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + integer :: i + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Configuration and mesh + print *, "1. Testing basic systems..." + print *, "-----------------------------" + call config_print(config) + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Creating reconstructors directly + print *, "2. Creating reconstructors..." + print *, "------------------------------" + eno = eno_reconstructor(name="ENO", order=3, epsilon=1.0e-6_wp) + weno3 = weno3_reconstructor(name="WENO3", order=3, epsilon=1.0e-6_wp) + + call eno%info() + call weno3%info() + print *, "" + + ! Test 3: Creating flux calculator directly + print *, "3. Creating flux calculator..." + print *, "-------------------------------" + rusanov = rusanov_flux(name="Rusanov", wave_speed_default=1.0_wp) + call rusanov%info() + print *, "" + + ! Test 4: Registry integration + print *, "4. Testing registry..." + print *, "----------------------" + call initialize_registry(verbose=.true.) + + ! Register with simple method + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! Check if registered + if (has_component("reconstructor", "eno")) then + print *, "✓ ENO reconstructor registered successfully" + else + print *, "✗ ENO reconstructor registration failed" + end if + + if (has_component("flux", "rusanov")) then + print *, "✓ Rusanov flux registered successfully" + else + print *, "✗ Rusanov flux registration failed" + end if + + print *, "" + call component_registry%list_all() + print *, "" + + ! Test getting available components + print *, "5. Testing component listing..." + print *, "--------------------------------" + call test_component_listing() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Factory pattern simple test completed successfully ===" + +contains + + subroutine test_component_listing() + character(len=:), allocatable :: recon_names(:) + integer, allocatable :: recon_orders(:) + integer :: i + + print *, "Available reconstructors:" + call get_available_components("reconstructor", recon_names, recon_orders) + + if (allocated(recon_names)) then + do i = 1, size(recon_names) + print *, " - ", trim(recon_names(i)) + end do + print *, "Total reconstructors: ", size(recon_names) + else + print *, " (no reconstructors found)" + end if + end subroutine test_component_listing + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/tests/test_minimal.f90 b/example/1d-linear-convection/weno3/fortran/registry/01g/tests/test_minimal.f90 new file mode 100644 index 00000000..bb4b7cda --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/tests/test_minimal.f90 @@ -0,0 +1,108 @@ +! tests/test_minimal.f90 +program test_minimal + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i ! Declare loop variable here + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", component_registry%size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components + print *, "5. Testing available components" + print *, "--------------------------------" + + ! Use a separate block to avoid allocation issues + call test_available_components() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Minimal test completed successfully ===" + +contains + + ! Internal subroutine for testing available components + subroutine test_available_components() + character(len=:), allocatable :: names(:) + integer, allocatable :: orders(:) + integer :: j + + call get_available_components("reconstructor", names, orders) + + if (allocated(names)) then + print *, "Reconstructors:" + do j = 1, size(names) + print *, " - ", trim(names(j)) + if (allocated(orders)) then + print *, " Order: ", orders(j) + end if + end do + else + print *, "No reconstructors found" + end if + end subroutine test_available_components + +end program test_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/tests/test_minimal_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/01g/tests/test_minimal_simple.f90 new file mode 100644 index 00000000..bd6368cd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/tests/test_minimal_simple.f90 @@ -0,0 +1,5 @@ +! tests/test_minimal_simple.f90 +program test_minimal_simple + implicit none + +end program test_minimal_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01g/tests/test_minimal_simpleBAK.f90 b/example/1d-linear-convection/weno3/fortran/registry/01g/tests/test_minimal_simpleBAK.f90 new file mode 100644 index 00000000..689165de --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01g/tests/test_minimal_simpleBAK.f90 @@ -0,0 +1,84 @@ +! tests/test_minimal_simple.f90 +program test_minimal_simple + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Simple Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components (simple version) + print *, "5. Testing registry functionality" + print *, "---------------------------------" + print *, "Registry initialized: ", registry_is_initialized() + print *, "Number of components: ", registry_get_size() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Simple test completed successfully ===" + +end program test_minimal_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01h/CMakeLists.txt new file mode 100644 index 00000000..ef66d584 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/README.md b/example/1d-linear-convection/weno3/fortran/registry/01h/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/01h/scripts/build.bat new file mode 100644 index 00000000..d243695b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/scripts/build.bat @@ -0,0 +1,26 @@ +@echo off +echo ======================================== +echo Fortran CFD Project Builder +echo (Wrapper for Python script) +echo ======================================== +echo. + +REM 检查Python +where python >nul 2>nul +if %errorlevel% neq 0 ( + echo [ERROR] Python not found. Please install Python 3. + pause + exit /b 1 +) + +REM 运行Python构建脚本 +echo [INFO] Running Python build script... +python build.py %* + +if %errorlevel% neq 0 ( + echo [ERROR] Build failed + pause + exit /b 1 +) + +pause \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/scripts/build.ps1 b/example/1d-linear-convection/weno3/fortran/registry/01h/scripts/build.ps1 new file mode 100644 index 00000000..4497c5eb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/scripts/build.ps1 @@ -0,0 +1,32 @@ +# Fortran CFD Project Builder (PowerShell wrapper) + +Write-Host "========================================" -ForegroundColor Cyan +Write-Host " Fortran CFD Project Builder" -ForegroundColor Cyan +Write-Host " (PowerShell wrapper for Python script)" -ForegroundColor Cyan +Write-Host "========================================" -ForegroundColor Cyan +Write-Host "" + +# 检查Python +$python = Get-Command python -ErrorAction SilentlyContinue +if (-not $python) { + $python = Get-Command python3 -ErrorAction SilentlyContinue +} + +if (-not $python) { + Write-Host "[ERROR] Python not found. Please install Python 3." -ForegroundColor Red + Read-Host "Press Enter to exit" + exit 1 +} + +# 运行Python构建脚本 +Write-Host "[INFO] Running Python build script..." -ForegroundColor Yellow +$argsString = $args -join ' ' +$command = "python build.py $argsString" + +Invoke-Expression $command + +if ($LASTEXITCODE -ne 0) { + Write-Host "[ERROR] Build failed" -ForegroundColor Red + Read-Host "Press Enter to exit" + exit 1 +} \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/01h/scripts/build.py new file mode 100644 index 00000000..1c99c2c8 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/scripts/build.py @@ -0,0 +1,357 @@ +#!/usr/bin/env python3 +""" +Fortran CFD 项目构建脚本 (Python版) +支持 Intel oneAPI 环境的自动化构建 +""" + +import os +import sys +import subprocess +import shutil +from pathlib import Path +import argparse +import platform +import time + +class Colors: + """终端颜色""" + if platform.system() == "Windows": + # Windows 启用 ANSI + os.system("") + + HEADER = '\033[95m' + BLUE = '\033[94m' + CYAN = '\033[96m' + GREEN = '\033[92m' + YELLOW = '\033[93m' + RED = '\033[91m' + MAGENTA = '\033[95m' + DARK_GRAY = '\033[90m' # 添加这个 + END = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + +def print_color(text, color=Colors.END): + """彩色打印""" + print(f"{color}{text}{Colors.END}") + +def print_header(text): + """打印标题""" + print_color("\n" + "="*60, Colors.CYAN) + print_color(f" {text}", Colors.BOLD + Colors.CYAN) + print_color("="*60 + "\n", Colors.CYAN) + +def print_step(step, total, message): + """打印步骤""" + print_color(f"[{step}/{total}] {message}...", Colors.YELLOW) + +def print_success(message): + """打印成功""" + print_color(f"✓ {message}", Colors.GREEN) + +def print_error(message): + """打印错误""" + print_color(f"✗ {message}", Colors.RED) + +def print_warning(message): + """打印警告""" + print_color(f"! {message}", Colors.YELLOW) + +def run_command(cmd, cwd=None, check=True, capture=True): + """运行命令""" + print_color(f" $ {' '.join(cmd)}", Colors.BLUE) + + try: + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=capture, + text=True, + encoding='utf-8', + errors='ignore', + shell=False + ) + + if capture and result.stdout: + print(result.stdout) + if capture and result.stderr: + print_color(result.stderr, Colors.YELLOW) + + if check and result.returncode != 0: + print_error(f"Command failed with exit code: {result.returncode}") + return False + + return True + + except FileNotFoundError as e: + print_error(f"Command not found: {cmd[0]}") + if check: + raise + return False + except Exception as e: + print_error(f"Command execution failed: {e}") + return False + +def setup_intel_environment(): + """设置 Intel oneAPI 环境""" + setvars_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + ] + + setvars_path = None + for path in setvars_paths: + if os.path.exists(path): + setvars_path = path + break + + if not setvars_path: + print_error("Intel oneAPI setvars.bat not found.") + print_warning("Please install Intel oneAPI or update the path in build.py") + return False + + # 创建临时的 batch 文件 + temp_bat = "temp_setvars.bat" + with open(temp_bat, 'w') as f: + f.write(f'@echo off\n') + f.write(f'call "{setvars_path}" > nul 2>&1\n') + f.write(f'set\n') + + try: + # 运行并捕获环境变量 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + shell=True, + encoding='utf-8', + errors='ignore' + ) + + # 解析并设置环境变量 + for line in result.stdout.split('\n'): + if '=' in line: + key, value = line.split('=', 1) + os.environ[key.strip()] = value.strip() + + os.remove(temp_bat) + print_success("Intel oneAPI environment configured") + return True + + except Exception as e: + print_error(f"Failed to setup Intel environment: {e}") + if os.path.exists(temp_bat): + os.remove(temp_bat) + return False + +def build_project(args): + """构建项目主函数""" + start_time = time.time() + + print_header(f"Fortran CFD Project Builder") + print_color(f"Build type: {args.build_type}", Colors.CYAN) + print_color(f"Run tests: {args.run_tests}", Colors.CYAN) + print() + + # 获取项目根目录(脚本所在目录的父目录) + script_dir = Path(__file__).parent + project_root = script_dir.parent + os.chdir(project_root) + + print_color(f"Project root: {project_root}", Colors.BLUE) + + # 步骤1: 设置 Intel 环境 + print_step(1, 4, "Setting up Intel Fortran compiler") + if not setup_intel_environment(): + return False + + # 步骤2: 准备构建目录 + print_step(2, 4, "Preparing build directory") + build_dir = project_root / "build" + + if args.clean and build_dir.exists(): + try: + shutil.rmtree(build_dir) + print_success("Cleaned build directory") + except Exception as e: + print_error(f"Failed to clean build directory: {e}") + if not args.force: + return False + + build_dir.mkdir(exist_ok=True) + os.chdir(build_dir) + + # 步骤3: 配置项目 + print_step(3, 4, "Configuring project") + + cmake_cmd = [ + "cmake", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-T", "fortran=ifx", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ".." + ] + + if not run_command(cmake_cmd, check=not args.force): + if not args.force: + return False + + # 步骤4: 构建项目 + print_step(4, 4, "Building project") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + f"-j{args.jobs}" if args.jobs > 1 else "" + ] + + print(f"build_cmd={build_cmd}") + build_cmd = [c for c in build_cmd if c] # 移除空字符串 + + if not run_command(build_cmd, check=not args.force): + if not args.force: + return False + + build_time = time.time() - start_time + print_success(f"Build completed in {build_time:.1f} seconds") + + # 运行测试 + if args.run_tests: + print_header("Running Tests") + run_tests(args.build_type) + + print_header("Build Summary") + print_color(f"Build directory: {build_dir}", Colors.GREEN) + print_color(f"Build type: {args.build_type}", Colors.GREEN) + print_color(f"Total time: {build_time:.1f}s", Colors.GREEN) + + return True + +def run_tests(build_type): + """运行测试""" + tests = [ + ("test_simple", "Simple functionality test"), + ("test_factory", "Factory pattern test"), + ] + + for test_name, description in tests: + # 修复:在这里直接访问当前循环的 test_name + _run_single_test(test_name, description, build_type) + +def _run_single_test(test_name, description, build_type): + """运行单个测试(修复作用域问题)""" + # 可能的测试可执行文件路径 + possible_paths = [ + Path(f"./{build_type}/{test_name}.exe"), + Path(f"./tests/{build_type}/{test_name}.exe"), + Path(f"./tests/{test_name}.exe"), + Path(f"{test_name}.exe"), + Path(f"./{test_name}.exe"), + ] + + test_exe = None + + # 尝试所有可能的路径 + for path in possible_paths: + if path.exists(): + test_exe = path + break + + if test_exe: + print_color(f"\n[TEST] {description}...", Colors.MAGENTA) + print_color("-" * 50, Colors.DARK_GRAY) + + try: + result = subprocess.run( + [str(test_exe)], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + + if result.stdout: + print(result.stdout) + + if result.returncode == 0: + print_success(f"{test_name} passed") + else: + print_error(f"{test_name} failed (exit code: {result.returncode})") + if result.stderr: + print_color(result.stderr, Colors.YELLOW) + + except Exception as e: + print_error(f"{test_name} failed: {e}") + else: + print_warning(f"{test_name}.exe not found. Searched paths:") + for path in possible_paths: + print_color(f" - {path}", Colors.YELLOW) + +def main(): + """主函数""" + parser = argparse.ArgumentParser(description="Fortran CFD Project Builder") + + parser.add_argument( + "--build-type", + choices=["Debug", "Release", "RelWithDebInfo", "MinSizeRel"], + default="Debug", + help="Build type (default: Debug)" + ) + + parser.add_argument( + "--clean", + action="store_true", + help="Clean build directory before building" + ) + + parser.add_argument( + "--no-tests", + action="store_true", + help="Skip running tests" + ) + + parser.add_argument( + "-j", "--jobs", + type=int, + default=0, + help="Number of parallel jobs (0 = use all cores)" + ) + + parser.add_argument( + "--force", + action="store_true", + help="Continue on errors" + ) + + parser.add_argument( + "--verbose", + action="store_true", + help="Verbose output" + ) + + args = parser.parse_args() + + if args.jobs == 0: + import multiprocessing + args.jobs = multiprocessing.cpu_count() + + args.run_tests = not args.no_tests + + try: + success = build_project(args) + if not success and not args.force: + sys.exit(1) + except KeyboardInterrupt: + print_color("\nBuild interrupted by user", Colors.YELLOW) + sys.exit(1) + except Exception as e: + print_error(f"Unexpected error: {e}") + if args.verbose: + import traceback + traceback.print_exc() + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/01h/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01h/src/CMakeLists.txt new file mode 100644 index 00000000..ee38952b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/src/CMakeLists.txt @@ -0,0 +1,14 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01h/src/core/CMakeLists.txt new file mode 100644 index 00000000..376dd4fe --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/src/core/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/core/CMakeLists.txt +message(STATUS "配置核心模块...") + +add_library(core + registry.f90 + factory_interfaces.f90 +) + +target_include_directories(core PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 安装规则 +install(TARGETS core + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) +install(FILES + ${CMAKE_Fortran_MODULE_DIRECTORY}/registry_module.mod + ${CMAKE_Fortran_MODULE_DIRECTORY}/factory_interfaces.mod + DESTINATION include/fortran_cfd/core +) + +message(STATUS "核心模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/01h/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..19cc2c59 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/01h/src/core/registry.f90 new file mode 100644 index 00000000..f996c89b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/src/core/registry.f90 @@ -0,0 +1,318 @@ +! src/core/registry.f90 +module registry_module + use, intrinsic :: iso_fortran_env, only: real64 + use factory_interfaces, only: factory_procedure + implicit none + + private + + ! Public interface + public :: real64, component_info, component_registry + public :: register_component_simple, initialize_registry, cleanup_registry + public :: has_component, get_available_components + public :: registry_is_initialized, registry_get_size ! 添加公共访问方法 + + ! Type definitions (simplified, no factory for now) + type :: component_info + character(len=32) :: category = "" + character(len=32) :: name = "" + integer :: order = 0 + contains + procedure :: print => ci_print + end type component_info + + type :: component_registry_type + private + type(component_info), allocatable :: components(:) + integer :: count = 0 + integer :: capacity = 100 + logical :: verbose = .true. + logical :: initialized = .false. + contains + procedure :: register => cr_register + procedure :: get => cr_get + procedure :: list_all => cr_list_all + procedure :: clear => cr_clear + procedure :: size => cr_size + procedure :: is_initialized => cr_is_initialized ! 内部方法 + end type component_registry_type + + ! Global registry instance + type(component_registry_type), save :: component_registry + +contains + + ! ==================== PUBLIC API ==================== + + ! Initialize registry + subroutine initialize_registry(initial_capacity, verbose) + integer, optional, intent(in) :: initial_capacity + logical, optional, intent(in) :: verbose + + if (component_registry%initialized) then + if (component_registry%verbose) then + print *, "[INFO] Registry already initialized" + end if + return + end if + + if (present(initial_capacity)) then + component_registry%capacity = max(10, initial_capacity) + end if + + if (present(verbose)) then + component_registry%verbose = verbose + end if + + ! Allocate array + allocate(component_registry%components(component_registry%capacity)) + + component_registry%initialized = .true. + component_registry%count = 0 + + if (component_registry%verbose) then + print *, "[INIT] Registry initialized, capacity:", component_registry%capacity + end if + end subroutine initialize_registry + + ! Cleanup registry + subroutine cleanup_registry + call component_registry%clear() + if (component_registry%verbose) then + print *, "[CLEANUP] Registry cleaned up" + end if + end subroutine cleanup_registry + + ! Simple registration (no factory) + subroutine register_component_simple(category, name) + character(len=*), intent(in) :: category, name + + type(component_info) :: info + + info%category = to_lower(trim(adjustl(category))) + info%name = to_lower(trim(adjustl(name))) + info%order = 0 + + call component_registry%register(info) + end subroutine register_component_simple + + ! Check if component exists + function has_component(category, name) result(found) + character(len=*), intent(in) :: category, name + logical :: found + + type(component_info) :: info + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + info = component_registry%get(cat_lower, name_lower) + found = (len_trim(info%category) > 0) + end function has_component + + ! Get available components in a category + subroutine get_available_components(category, names, orders) + character(len=*), intent(in) :: category + character(len=:), allocatable, intent(out), optional :: names(:) + integer, allocatable, intent(out), optional :: orders(:) + + character(len=32) :: cat_lower + integer :: i, count, idx + type(component_info) :: info + + cat_lower = to_lower(trim(adjustl(category))) + + ! Count components in this category + count = 0 + do i = 1, component_registry%count + if (component_registry%components(i)%category == cat_lower) then + count = count + 1 + end if + end do + + ! Allocate arrays if requested + if (present(names)) then + allocate(character(len=32) :: names(count)) + end if + + if (present(orders)) then + allocate(orders(count)) + end if + + ! Fill arrays + idx = 1 + do i = 1, component_registry%count + if (component_registry%components(i)%category == cat_lower) then + info = component_registry%components(i) + if (present(names)) then + names(idx) = info%name + end if + if (present(orders)) then + orders(idx) = info%order + end if + idx = idx + 1 + end if + end do + end subroutine get_available_components + + ! Public function to check if registry is initialized + function registry_is_initialized() result(is_initialized) + logical :: is_initialized + is_initialized = component_registry%is_initialized() + end function registry_is_initialized + + ! Public function to get registry size + function registry_get_size() result(size_val) + integer :: size_val + size_val = component_registry%size() + end function registry_get_size + + ! ==================== COMPONENT INFO METHODS ==================== + + subroutine ci_print(this) + class(component_info), intent(in) :: this + + if (this%order > 0) then + print *, " [", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")]" + else + print *, " [", trim(this%category), ".", trim(this%name), "]" + end if + end subroutine ci_print + + ! ==================== REGISTRY INTERNAL METHODS ==================== + + subroutine cr_register(this, info) + class(component_registry_type), intent(inout) :: this + type(component_info), intent(in) :: info + + type(component_info), allocatable :: temp(:) + integer :: i + + if (.not. this%initialized) then + error stop "[ERROR] Registry not initialized, call initialize_registry first" + end if + + ! Check if already exists + do i = 1, this%count + if (this%components(i)%category == info%category .and. & + this%components(i)%name == info%name) then + if (this%verbose) then + print *, "[WARN] Overwriting: ", & + trim(info%category), ".", trim(info%name) + end if + this%components(i) = info + return + end if + end do + + ! Expand array if needed + if (this%count >= this%capacity) then + this%capacity = this%capacity * 2 + allocate(temp(this%capacity)) + temp(1:this%count) = this%components(1:this%count) + call move_alloc(temp, this%components) + + if (this%verbose) then + print *, "[INFO] Registry expanded to capacity:", this%capacity + end if + end if + + ! Add component + this%count = this%count + 1 + this%components(this%count) = info + + if (this%verbose) then + print *, "[OK] Registered: ", trim(info%category), ".", trim(info%name) + end if + end subroutine cr_register + + function cr_get(this, category, name) result(info) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + type(component_info) :: info + + integer :: i + + ! Initialize return value as empty + info%category = "" + info%name = "" + info%order = 0 + + if (.not. this%initialized) then + return + end if + + do i = 1, this%count + if (this%components(i)%category == category .and. & + this%components(i)%name == name) then + info = this%components(i) + return + end if + end do + end function cr_get + + subroutine cr_list_all(this) + class(component_registry_type), intent(in) :: this + integer :: i + + if (.not. this%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + print *, "=== Registry Contents (", this%count, " components) ===" + + if (this%count == 0) then + print *, " (empty)" + return + end if + + ! Show components grouped by category + do i = 1, this%count + call this%components(i)%print() + end do + + print *, "===========================================" + end subroutine cr_list_all + + subroutine cr_clear(this) + class(component_registry_type), intent(inout) :: this + + if (allocated(this%components)) then + deallocate(this%components) + end if + + this%count = 0 + this%capacity = 100 + this%initialized = .false. + end subroutine cr_clear + + integer function cr_size(this) + class(component_registry_type), intent(in) :: this + cr_size = this%count + end function cr_size + + logical function cr_is_initialized(this) + class(component_registry_type), intent(in) :: this + cr_is_initialized = this%initialized + end function cr_is_initialized + + ! ==================== UTILITY FUNCTIONS ==================== + + function to_lower(str) result(lower_str) + character(len=*), intent(in) :: str + character(len=len(str)) :: lower_str + integer :: i + + do i = 1, len(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + lower_str(i:i) = char(ichar(str(i:i)) + 32) + else + lower_str(i:i) = str(i:i) + end if + end do + end function to_lower + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01h/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..2b72c548 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/src/infrastructure/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "配置基础设施模块...") + +add_library(infrastructure + config.f90 + mesh.f90 +) + +target_link_libraries(infrastructure PUBLIC core) + +target_include_directories(infrastructure PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS infrastructure + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "基础设施模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/01h/src/infrastructure/config.f90 new file mode 100644 index 00000000..34019f2f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/src/infrastructure/config.f90 @@ -0,0 +1,90 @@ +! src/infrastructure/config.f90 +module config_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, cfd_config, config_print, config_with_reconstruction + + ! CFD configuration type + type :: cfd_config + character(len=20) :: ic_type = "step" + character(len=20) :: recon_scheme = "eno" + character(len=20) :: flux_type = "rusanov" + integer :: rk_order = 1 + real(real64) :: wave_speed = 1.0_real64 + real(real64) :: final_time = 0.625_real64 + real(real64) :: dt = 0.025_real64 + character(len=20) :: boundary_type = "periodic" + real(real64) :: left_boundary_value = 1.0_real64 + real(real64) :: right_boundary_value = 2.0_real64 + integer :: spatial_order = 2 + logical :: verbose = .true. + end type cfd_config + +contains + + subroutine config_print(this) + type(cfd_config), intent(in) :: this + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(this%ic_type) + print *, "Reconstruction: ", trim(this%recon_scheme), " (order:", this%spatial_order, ")" + print *, "Flux type: ", trim(this%flux_type) + print *, "Time integration: RK", this%rk_order + print *, "Wave speed: ", this%wave_speed + print *, "Final time: ", this%final_time + print *, "Time step: ", this%dt + print *, "Boundary: ", trim(this%boundary_type) + if (trim(this%boundary_type) == 'dirichlet') then + print *, " Dirichlet values: [", this%left_boundary_value, ", ", & + this%right_boundary_value, "]" + end if + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(this, scheme, order) + type(cfd_config), intent(inout) :: this + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + character(len=20) :: scheme_lower + + ! Convert to lowercase + scheme_lower = scheme + call to_lower_inplace(scheme_lower) + this%recon_scheme = trim(adjustl(scheme_lower)) + + ! Set order + if (present(order)) then + this%spatial_order = order + else + ! Smart defaults + if (index(this%recon_scheme, 'weno') > 0) then + this%spatial_order = 5 + else if (trim(this%recon_scheme) == 'eno') then + this%spatial_order = 3 + else + print *, "[ERROR] Unsupported reconstruction scheme: ", trim(this%recon_scheme) + return + end if + end if + + if (this%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(this%recon_scheme), & + " Order: ", this%spatial_order + end if + end subroutine config_with_reconstruction + + subroutine to_lower_inplace(str) + character(len=*), intent(inout) :: str + integer :: i + + do i = 1, len_trim(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + str(i:i) = char(ichar(str(i:i)) + 32) + end if + end do + end subroutine to_lower_inplace + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/01h/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..65a45dcd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/src/infrastructure/mesh.f90 @@ -0,0 +1,74 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, mesh_type, mesh_init, mesh_print_info + + ! 网格类型 + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer :: ncells = 40 + integer :: nnodes + integer :: nx + real(wp), allocatable :: x(:) ! 节点坐标 + real(wp), allocatable :: xcc(:) ! 单元中心坐标 + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer, optional, intent(in) :: ncells + + integer :: i + + ! 设置参数 + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! 计算派生参数 + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! 分配数组 + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! 生成节点坐标 + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! 生成单元中心坐标 + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== 网格信息 ===" + print *, "计算域: [", this%xmin, ", ", this%xmax, "]" + print *, "单元数: ", this%ncells + print *, "节点数: ", this%nnodes + print *, "网格尺寸 dx: ", this%dx + print *, "域长度 L: ", this%L + print *, "==========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01h/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01h/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..b2617f29 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux + base.f90 +) + +target_link_libraries(flux PUBLIC core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/01h/src/numerics/flux/base.f90 new file mode 100644 index 00000000..d9bd640c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/src/numerics/flux/base.f90 @@ -0,0 +1,24 @@ +! src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/01h/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..4c1054ab --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/src/numerics/flux/rusanov.f90 @@ -0,0 +1,48 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + + ! 添加构造函数接口 + interface rusanov_flux + module procedure create_rusanov_flux + end interface + +contains + + ! 构造函数 + type(rusanov_flux) function create_rusanov_flux(name, wave_speed_default) result(this) + character(len=*), optional, intent(in) :: name + real(real64), optional, intent(in) :: wave_speed_default + + if (present(name)) then + this%name = name + else + this%name = "Rusanov" + end if + + if (present(wave_speed_default)) then + this%wave_speed_default = wave_speed_default + else + this%wave_speed_default = 1.0_real64 + end if + end function create_rusanov_flux + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + call flux_info(this) + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01h/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..77808c1a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor + base.f90 +) + +target_link_libraries(reconstructor PUBLIC core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/01h/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..404535d1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/src/numerics/reconstructor/base.f90 @@ -0,0 +1,40 @@ +! src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + procedure :: reconstruct => reconstruct_default ! 添加这个方法 + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + + ! 默认的重构方法 + subroutine reconstruct_default(this, q, qL, qR) + class(reconstructor_base), intent(in) :: this + real(real64), intent(in) :: q(0:) ! 包含ghost cells + real(real64), intent(out) :: qL(:), qR(:) + integer :: i, n + + n = size(qL) + do i = 1, n + qL(i) = q(i) ! 简单的一阶重构 + qR(i) = q(i+1) + end do + end subroutine reconstruct_default +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/01h/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..8bc206ac --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,56 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + + ! 添加构造函数接口 + interface eno_reconstructor + module procedure create_eno_reconstructor + end interface + +contains + + ! 构造函数 + type(eno_reconstructor) function create_eno_reconstructor(name, order, epsilon) result(this) + character(len=*), optional, intent(in) :: name + integer, optional, intent(in) :: order + real(real64), optional, intent(in) :: epsilon + + ! 设置默认值 + if (present(name)) then + this%name = name + else + this%name = "ENO" + end if + + if (present(order)) then + this%order = order + else + this%order = 3 + end if + + if (present(epsilon)) then + this%epsilon = epsilon + else + this%epsilon = 1.0e-6_real64 + end if + end function create_eno_reconstructor + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + call reconstructor_info(this) + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/01h/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..ffd50016 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,55 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno3_reconstructor + + type, extends(reconstructor_base) :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + + ! 添加构造函数接口 + interface weno3_reconstructor + module procedure create_weno3_reconstructor + end interface + +contains + + ! 构造函数 + type(weno3_reconstructor) function create_weno3_reconstructor(name, order, epsilon) result(this) + character(len=*), optional, intent(in) :: name + integer, optional, intent(in) :: order + real(real64), optional, intent(in) :: epsilon + + if (present(name)) then + this%name = name + else + this%name = "WENO3" + end if + + if (present(order)) then + this%order = order + else + this%order = 3 + end if + + if (present(epsilon)) then + this%epsilon = epsilon + else + this%epsilon = 1.0e-6_real64 + end if + end function create_weno3_reconstructor + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + call reconstructor_info(this) + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01h/tests/CMakeLists.txt new file mode 100644 index 00000000..2df858db --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/tests/CMakeLists.txt @@ -0,0 +1,17 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +add_executable(test_minimal_simple test_minimal_simple.f90) + +message(STATUS "CMAKE_Fortran_MODULE_DIRECTORY=${CMAKE_Fortran_MODULE_DIRECTORY}") + +#target_include_directories( test_minimal_simple +# PRIVATE +# ${CMAKE_Fortran_MODULE_DIRECTORY} +#) + +target_link_libraries( test_minimal_simple + PRIVATE + infrastructure +) + diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/tests/test_factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/01h/tests/test_factory.f90 new file mode 100644 index 00000000..8968b9c2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/tests/test_factory.f90 @@ -0,0 +1,154 @@ +! tests/test_factory.f90 +program test_factory + use registry_module + use config_module + use mesh_module + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + class(*), allocatable :: instance + class(reconstructor_base), allocatable :: recon + class(flux_calculator_base), allocatable :: flux_calc + real(wp), allocatable :: q(:), qL(:), qR(:), flux(:) + integer :: i, n + + print *, "=== Factory Pattern Test ===" + print *, "" + + ! Initialize systems + call initialize_registry(verbose=.true.) + + ! Register components with factories + print *, "1. Registering components with factories..." + call register_component_with_factory("reconstructor", "eno", create_eno, 3) + call register_component_with_factory("reconstructor", "weno3", create_weno3, 3) + call register_component_with_factory("flux", "rusanov", create_rusanov) + + call component_registry%list_all() + print *, "" + + ! Test creating ENO reconstructor + print *, "2. Creating ENO reconstructor..." + call create_component("reconstructor", "eno", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (eno_reconstructor) + allocate(recon, source=inst) + print *, "ENO reconstructor created successfully" + call recon%info() + print *, "" + + ! Test reconstruction + n = 10 + allocate(q(0:n+1), qL(n), qR(n)) + + ! Initialize test data (sine wave) + do i = 0, n+1 + q(i) = sin(2.0_wp * 3.141592653589793_wp * real(i-1, wp) / real(n, wp)) + end do + + print *, "Testing ENO reconstruction..." + call recon%reconstruct(q, qL, qR) + + print *, "q (internal):" + do i = 1, n + write(*, '(I3, F10.6)') i, q(i) + end do + + print *, "qL (left interface values):" + do i = 1, n + write(*, '(I3, F10.6)') i, qL(i) + end do + + deallocate(q, qL, qR) + class default + print *, "[ERROR] Wrong type for ENO reconstructor" + end select + else + print *, "[ERROR] Failed to create ENO reconstructor" + end if + print *, "" + + ! Test creating WENO3 reconstructor + print *, "3. Creating WENO3 reconstructor..." + if (allocated(instance)) deallocate(instance) + call create_component("reconstructor", "weno3", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (weno3_reconstructor) + if (allocated(recon)) deallocate(recon) + allocate(recon, source=inst) + print *, "WENO3 reconstructor created successfully" + call recon%info() + class default + print *, "[ERROR] Wrong type for WENO3 reconstructor" + end select + else + print *, "[ERROR] Failed to create WENO3 reconstructor" + end if + print *, "" + + ! Test creating Rusanov flux calculator + print *, "4. Creating Rusanov flux calculator..." + if (allocated(instance)) deallocate(instance) + call create_component("flux", "rusanov", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (rusanov_flux) + allocate(flux_calc, source=inst) + print *, "Rusanov flux calculator created successfully" + call flux_calc%info() + print *, "" + + ! Test flux computation + n = 5 + allocate(qL(n), qR(n), flux(n)) + + ! Initialize test data + do i = 1, n + qL(i) = 1.0_wp + 0.1_wp * real(i-1, wp) + qR(i) = 1.0_wp + 0.1_wp * real(i, wp) + end do + + print *, "Testing Rusanov flux computation..." + call flux_calc%compute(qL, qR, flux, 1.0_wp) + + print *, "qL:" + do i = 1, n + write(*, '(I3, F10.6)') i, qL(i) + end do + + print *, "qR:" + do i = 1, n + write(*, '(I3, F10.6)') i, qR(i) + end do + + print *, "Flux:" + do i = 1, n + write(*, '(I3, F10.6)') i, flux(i) + end do + + deallocate(qL, qR, flux) + class default + print *, "[ERROR] Wrong type for Rusanov flux" + end select + else + print *, "[ERROR] Failed to create Rusanov flux calculator" + end if + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Factory pattern test completed successfully ===" + +end program test_factory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/tests/test_factory_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/01h/tests/test_factory_simple.f90 new file mode 100644 index 00000000..c0dcde27 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/tests/test_factory_simple.f90 @@ -0,0 +1,106 @@ +! tests/test_factory_simple.f90 +program test_factory_simple + use registry_module + use config_module + use mesh_module + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + integer :: i + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Configuration and mesh + print *, "1. Testing basic systems..." + print *, "-----------------------------" + call config_print(config) + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Creating reconstructors directly + print *, "2. Creating reconstructors..." + print *, "------------------------------" + eno = eno_reconstructor(name="ENO", order=3, epsilon=1.0e-6_wp) + weno3 = weno3_reconstructor(name="WENO3", order=3, epsilon=1.0e-6_wp) + + call eno%info() + call weno3%info() + print *, "" + + ! Test 3: Creating flux calculator directly + print *, "3. Creating flux calculator..." + print *, "-------------------------------" + rusanov = rusanov_flux(name="Rusanov", wave_speed_default=1.0_wp) + call rusanov%info() + print *, "" + + ! Test 4: Registry integration + print *, "4. Testing registry..." + print *, "----------------------" + call initialize_registry(verbose=.true.) + + ! Register with simple method + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! Check if registered + if (has_component("reconstructor", "eno")) then + print *, "✓ ENO reconstructor registered successfully" + else + print *, "✗ ENO reconstructor registration failed" + end if + + if (has_component("flux", "rusanov")) then + print *, "✓ Rusanov flux registered successfully" + else + print *, "✗ Rusanov flux registration failed" + end if + + print *, "" + call component_registry%list_all() + print *, "" + + ! Test getting available components + print *, "5. Testing component listing..." + print *, "--------------------------------" + call test_component_listing() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Factory pattern simple test completed successfully ===" + +contains + + subroutine test_component_listing() + character(len=:), allocatable :: recon_names(:) + integer, allocatable :: recon_orders(:) + integer :: i + + print *, "Available reconstructors:" + call get_available_components("reconstructor", recon_names, recon_orders) + + if (allocated(recon_names)) then + do i = 1, size(recon_names) + print *, " - ", trim(recon_names(i)) + end do + print *, "Total reconstructors: ", size(recon_names) + else + print *, " (no reconstructors found)" + end if + end subroutine test_component_listing + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/tests/test_minimal.f90 b/example/1d-linear-convection/weno3/fortran/registry/01h/tests/test_minimal.f90 new file mode 100644 index 00000000..bb4b7cda --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/tests/test_minimal.f90 @@ -0,0 +1,108 @@ +! tests/test_minimal.f90 +program test_minimal + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i ! Declare loop variable here + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", component_registry%size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components + print *, "5. Testing available components" + print *, "--------------------------------" + + ! Use a separate block to avoid allocation issues + call test_available_components() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Minimal test completed successfully ===" + +contains + + ! Internal subroutine for testing available components + subroutine test_available_components() + character(len=:), allocatable :: names(:) + integer, allocatable :: orders(:) + integer :: j + + call get_available_components("reconstructor", names, orders) + + if (allocated(names)) then + print *, "Reconstructors:" + do j = 1, size(names) + print *, " - ", trim(names(j)) + if (allocated(orders)) then + print *, " Order: ", orders(j) + end if + end do + else + print *, "No reconstructors found" + end if + end subroutine test_available_components + +end program test_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/tests/test_minimal_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/01h/tests/test_minimal_simple.f90 new file mode 100644 index 00000000..689165de --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/tests/test_minimal_simple.f90 @@ -0,0 +1,84 @@ +! tests/test_minimal_simple.f90 +program test_minimal_simple + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Simple Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components (simple version) + print *, "5. Testing registry functionality" + print *, "---------------------------------" + print *, "Registry initialized: ", registry_is_initialized() + print *, "Number of components: ", registry_get_size() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Simple test completed successfully ===" + +end program test_minimal_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01h/tests/test_minimal_simpleBAK.f90 b/example/1d-linear-convection/weno3/fortran/registry/01h/tests/test_minimal_simpleBAK.f90 new file mode 100644 index 00000000..689165de --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01h/tests/test_minimal_simpleBAK.f90 @@ -0,0 +1,84 @@ +! tests/test_minimal_simple.f90 +program test_minimal_simple + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Simple Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components (simple version) + print *, "5. Testing registry functionality" + print *, "---------------------------------" + print *, "Registry initialized: ", registry_is_initialized() + print *, "Number of components: ", registry_get_size() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Simple test completed successfully ===" + +end program test_minimal_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01i/CMakeLists.txt new file mode 100644 index 00000000..ef66d584 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/README.md b/example/1d-linear-convection/weno3/fortran/registry/01i/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/01i/scripts/build.bat new file mode 100644 index 00000000..d243695b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/scripts/build.bat @@ -0,0 +1,26 @@ +@echo off +echo ======================================== +echo Fortran CFD Project Builder +echo (Wrapper for Python script) +echo ======================================== +echo. + +REM 检查Python +where python >nul 2>nul +if %errorlevel% neq 0 ( + echo [ERROR] Python not found. Please install Python 3. + pause + exit /b 1 +) + +REM 运行Python构建脚本 +echo [INFO] Running Python build script... +python build.py %* + +if %errorlevel% neq 0 ( + echo [ERROR] Build failed + pause + exit /b 1 +) + +pause \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/scripts/build.ps1 b/example/1d-linear-convection/weno3/fortran/registry/01i/scripts/build.ps1 new file mode 100644 index 00000000..4497c5eb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/scripts/build.ps1 @@ -0,0 +1,32 @@ +# Fortran CFD Project Builder (PowerShell wrapper) + +Write-Host "========================================" -ForegroundColor Cyan +Write-Host " Fortran CFD Project Builder" -ForegroundColor Cyan +Write-Host " (PowerShell wrapper for Python script)" -ForegroundColor Cyan +Write-Host "========================================" -ForegroundColor Cyan +Write-Host "" + +# 检查Python +$python = Get-Command python -ErrorAction SilentlyContinue +if (-not $python) { + $python = Get-Command python3 -ErrorAction SilentlyContinue +} + +if (-not $python) { + Write-Host "[ERROR] Python not found. Please install Python 3." -ForegroundColor Red + Read-Host "Press Enter to exit" + exit 1 +} + +# 运行Python构建脚本 +Write-Host "[INFO] Running Python build script..." -ForegroundColor Yellow +$argsString = $args -join ' ' +$command = "python build.py $argsString" + +Invoke-Expression $command + +if ($LASTEXITCODE -ne 0) { + Write-Host "[ERROR] Build failed" -ForegroundColor Red + Read-Host "Press Enter to exit" + exit 1 +} \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/01i/scripts/build.py new file mode 100644 index 00000000..1c99c2c8 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/scripts/build.py @@ -0,0 +1,357 @@ +#!/usr/bin/env python3 +""" +Fortran CFD 项目构建脚本 (Python版) +支持 Intel oneAPI 环境的自动化构建 +""" + +import os +import sys +import subprocess +import shutil +from pathlib import Path +import argparse +import platform +import time + +class Colors: + """终端颜色""" + if platform.system() == "Windows": + # Windows 启用 ANSI + os.system("") + + HEADER = '\033[95m' + BLUE = '\033[94m' + CYAN = '\033[96m' + GREEN = '\033[92m' + YELLOW = '\033[93m' + RED = '\033[91m' + MAGENTA = '\033[95m' + DARK_GRAY = '\033[90m' # 添加这个 + END = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + +def print_color(text, color=Colors.END): + """彩色打印""" + print(f"{color}{text}{Colors.END}") + +def print_header(text): + """打印标题""" + print_color("\n" + "="*60, Colors.CYAN) + print_color(f" {text}", Colors.BOLD + Colors.CYAN) + print_color("="*60 + "\n", Colors.CYAN) + +def print_step(step, total, message): + """打印步骤""" + print_color(f"[{step}/{total}] {message}...", Colors.YELLOW) + +def print_success(message): + """打印成功""" + print_color(f"✓ {message}", Colors.GREEN) + +def print_error(message): + """打印错误""" + print_color(f"✗ {message}", Colors.RED) + +def print_warning(message): + """打印警告""" + print_color(f"! {message}", Colors.YELLOW) + +def run_command(cmd, cwd=None, check=True, capture=True): + """运行命令""" + print_color(f" $ {' '.join(cmd)}", Colors.BLUE) + + try: + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=capture, + text=True, + encoding='utf-8', + errors='ignore', + shell=False + ) + + if capture and result.stdout: + print(result.stdout) + if capture and result.stderr: + print_color(result.stderr, Colors.YELLOW) + + if check and result.returncode != 0: + print_error(f"Command failed with exit code: {result.returncode}") + return False + + return True + + except FileNotFoundError as e: + print_error(f"Command not found: {cmd[0]}") + if check: + raise + return False + except Exception as e: + print_error(f"Command execution failed: {e}") + return False + +def setup_intel_environment(): + """设置 Intel oneAPI 环境""" + setvars_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + ] + + setvars_path = None + for path in setvars_paths: + if os.path.exists(path): + setvars_path = path + break + + if not setvars_path: + print_error("Intel oneAPI setvars.bat not found.") + print_warning("Please install Intel oneAPI or update the path in build.py") + return False + + # 创建临时的 batch 文件 + temp_bat = "temp_setvars.bat" + with open(temp_bat, 'w') as f: + f.write(f'@echo off\n') + f.write(f'call "{setvars_path}" > nul 2>&1\n') + f.write(f'set\n') + + try: + # 运行并捕获环境变量 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + shell=True, + encoding='utf-8', + errors='ignore' + ) + + # 解析并设置环境变量 + for line in result.stdout.split('\n'): + if '=' in line: + key, value = line.split('=', 1) + os.environ[key.strip()] = value.strip() + + os.remove(temp_bat) + print_success("Intel oneAPI environment configured") + return True + + except Exception as e: + print_error(f"Failed to setup Intel environment: {e}") + if os.path.exists(temp_bat): + os.remove(temp_bat) + return False + +def build_project(args): + """构建项目主函数""" + start_time = time.time() + + print_header(f"Fortran CFD Project Builder") + print_color(f"Build type: {args.build_type}", Colors.CYAN) + print_color(f"Run tests: {args.run_tests}", Colors.CYAN) + print() + + # 获取项目根目录(脚本所在目录的父目录) + script_dir = Path(__file__).parent + project_root = script_dir.parent + os.chdir(project_root) + + print_color(f"Project root: {project_root}", Colors.BLUE) + + # 步骤1: 设置 Intel 环境 + print_step(1, 4, "Setting up Intel Fortran compiler") + if not setup_intel_environment(): + return False + + # 步骤2: 准备构建目录 + print_step(2, 4, "Preparing build directory") + build_dir = project_root / "build" + + if args.clean and build_dir.exists(): + try: + shutil.rmtree(build_dir) + print_success("Cleaned build directory") + except Exception as e: + print_error(f"Failed to clean build directory: {e}") + if not args.force: + return False + + build_dir.mkdir(exist_ok=True) + os.chdir(build_dir) + + # 步骤3: 配置项目 + print_step(3, 4, "Configuring project") + + cmake_cmd = [ + "cmake", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-T", "fortran=ifx", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ".." + ] + + if not run_command(cmake_cmd, check=not args.force): + if not args.force: + return False + + # 步骤4: 构建项目 + print_step(4, 4, "Building project") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + f"-j{args.jobs}" if args.jobs > 1 else "" + ] + + print(f"build_cmd={build_cmd}") + build_cmd = [c for c in build_cmd if c] # 移除空字符串 + + if not run_command(build_cmd, check=not args.force): + if not args.force: + return False + + build_time = time.time() - start_time + print_success(f"Build completed in {build_time:.1f} seconds") + + # 运行测试 + if args.run_tests: + print_header("Running Tests") + run_tests(args.build_type) + + print_header("Build Summary") + print_color(f"Build directory: {build_dir}", Colors.GREEN) + print_color(f"Build type: {args.build_type}", Colors.GREEN) + print_color(f"Total time: {build_time:.1f}s", Colors.GREEN) + + return True + +def run_tests(build_type): + """运行测试""" + tests = [ + ("test_simple", "Simple functionality test"), + ("test_factory", "Factory pattern test"), + ] + + for test_name, description in tests: + # 修复:在这里直接访问当前循环的 test_name + _run_single_test(test_name, description, build_type) + +def _run_single_test(test_name, description, build_type): + """运行单个测试(修复作用域问题)""" + # 可能的测试可执行文件路径 + possible_paths = [ + Path(f"./{build_type}/{test_name}.exe"), + Path(f"./tests/{build_type}/{test_name}.exe"), + Path(f"./tests/{test_name}.exe"), + Path(f"{test_name}.exe"), + Path(f"./{test_name}.exe"), + ] + + test_exe = None + + # 尝试所有可能的路径 + for path in possible_paths: + if path.exists(): + test_exe = path + break + + if test_exe: + print_color(f"\n[TEST] {description}...", Colors.MAGENTA) + print_color("-" * 50, Colors.DARK_GRAY) + + try: + result = subprocess.run( + [str(test_exe)], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + + if result.stdout: + print(result.stdout) + + if result.returncode == 0: + print_success(f"{test_name} passed") + else: + print_error(f"{test_name} failed (exit code: {result.returncode})") + if result.stderr: + print_color(result.stderr, Colors.YELLOW) + + except Exception as e: + print_error(f"{test_name} failed: {e}") + else: + print_warning(f"{test_name}.exe not found. Searched paths:") + for path in possible_paths: + print_color(f" - {path}", Colors.YELLOW) + +def main(): + """主函数""" + parser = argparse.ArgumentParser(description="Fortran CFD Project Builder") + + parser.add_argument( + "--build-type", + choices=["Debug", "Release", "RelWithDebInfo", "MinSizeRel"], + default="Debug", + help="Build type (default: Debug)" + ) + + parser.add_argument( + "--clean", + action="store_true", + help="Clean build directory before building" + ) + + parser.add_argument( + "--no-tests", + action="store_true", + help="Skip running tests" + ) + + parser.add_argument( + "-j", "--jobs", + type=int, + default=0, + help="Number of parallel jobs (0 = use all cores)" + ) + + parser.add_argument( + "--force", + action="store_true", + help="Continue on errors" + ) + + parser.add_argument( + "--verbose", + action="store_true", + help="Verbose output" + ) + + args = parser.parse_args() + + if args.jobs == 0: + import multiprocessing + args.jobs = multiprocessing.cpu_count() + + args.run_tests = not args.no_tests + + try: + success = build_project(args) + if not success and not args.force: + sys.exit(1) + except KeyboardInterrupt: + print_color("\nBuild interrupted by user", Colors.YELLOW) + sys.exit(1) + except Exception as e: + print_error(f"Unexpected error: {e}") + if args.verbose: + import traceback + traceback.print_exc() + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/01i/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01i/src/CMakeLists.txt new file mode 100644 index 00000000..ee38952b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/src/CMakeLists.txt @@ -0,0 +1,14 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01i/src/core/CMakeLists.txt new file mode 100644 index 00000000..bcfd024d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/src/core/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/core/CMakeLists.txt +message(STATUS "配置核心模块...") + +add_library(core STATIC + registry.f90 + factory_interfaces.f90 +) + +target_include_directories(core PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 安装规则 +install(TARGETS core + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) +install(FILES + ${CMAKE_Fortran_MODULE_DIRECTORY}/registry_module.mod + ${CMAKE_Fortran_MODULE_DIRECTORY}/factory_interfaces.mod + DESTINATION include/fortran_cfd/core +) + +message(STATUS "核心模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/01i/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..19cc2c59 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/01i/src/core/registry.f90 new file mode 100644 index 00000000..49a30b61 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/src/core/registry.f90 @@ -0,0 +1,318 @@ +! src/core/registry.f90 +module registry_module + use, intrinsic :: iso_fortran_env, only: real64 + use factory_interfaces, only: factory_procedure + implicit none + + private + + ! Public interface + public :: real64, component_info, component_registry + public :: register_component_simple, initialize_registry, cleanup_registry + public :: has_component, get_available_components + public :: registry_is_initialized, registry_get_size ! 添加公共访问方法 + + ! Type definitions (simplified, no factory for now) + type :: component_info + character(len=32) :: category = "" + character(len=32) :: name = "" + integer :: order = 0 + contains + procedure :: print => ci_print + end type component_info + + type :: component_registry_type + private + type(component_info), allocatable :: components(:) + integer :: count = 0 + integer :: capacity = 100 + logical :: verbose = .true. + logical :: initialized = .false. + contains + procedure :: register => cr_register + procedure :: get => cr_get + procedure :: list_all => cr_list_all + procedure :: clear => cr_clear + procedure :: size => cr_size + procedure :: is_initialized => cr_is_initialized ! 内部方法 + end type component_registry_type + + ! Global registry instance + type(component_registry_type), save :: component_registry + +contains + + ! ==================== PUBLIC API ==================== + + ! Initialize registry + subroutine initialize_registry(initial_capacity, verbose) + integer, optional, intent(in) :: initial_capacity + logical, optional, intent(in) :: verbose + + if (component_registry%initialized) then + if (component_registry%verbose) then + print *, "[INFO] Registry already initialized" + end if + return + end if + + if (present(initial_capacity)) then + component_registry%capacity = max(10, initial_capacity) + end if + + if (present(verbose)) then + component_registry%verbose = verbose + end if + + ! Allocate array + allocate(component_registry%components(component_registry%capacity)) + + component_registry%initialized = .true. + component_registry%count = 0 + + if (component_registry%verbose) then + print *, "[INIT] Registry initialized, capacity:", component_registry%capacity + end if + end subroutine initialize_registry + + ! Cleanup registry + subroutine cleanup_registry + call component_registry%clear() + if (component_registry%verbose) then + print *, "[CLEANUP] Registry cleaned up" + end if + end subroutine cleanup_registry + + ! Simple registration (no factory) + subroutine register_component_simple(category, name) + character(len=*), intent(in) :: category, name + + type(component_info) :: info + + info%category = to_lower(trim(adjustl(category))) + info%name = to_lower(trim(adjustl(name))) + info%order = 0 + + call component_registry%register(info) + end subroutine register_component_simple + + ! Check if component exists + function has_component(category, name) result(found) + character(len=*), intent(in) :: category, name + logical :: found + + type(component_info) :: info + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + info = component_registry%get(cat_lower, name_lower) + found = (len_trim(info%category) > 0) + end function has_component + + ! Get available components in a category + subroutine get_available_components(category, names, orders) + character(len=*), intent(in) :: category + character(len=:), allocatable, intent(out), optional :: names(:) + integer, allocatable, intent(out), optional :: orders(:) + + character(len=32) :: cat_lower + integer :: i, count, idx + type(component_info) :: info + + cat_lower = to_lower(trim(adjustl(category))) + + ! Count components in this category + count = 0 + do i = 1, component_registry%count + if (component_registry%components(i)%category == cat_lower) then + count = count + 1 + end if + end do + + ! Allocate arrays if requested + if (present(names)) then + allocate(character(len=32) :: names(count)) + end if + + if (present(orders)) then + allocate(orders(count)) + end if + + ! Fill arrays + idx = 1 + do i = 1, component_registry%count + if (component_registry%components(i)%category == cat_lower) then + info = component_registry%components(i) + if (present(names)) then + names(idx) = info%name + end if + if (present(orders)) then + orders(idx) = info%order + end if + idx = idx + 1 + end if + end do + end subroutine get_available_components + + ! Public function to check if registry is initialized + function registry_is_initialized() result(is_initialized) + logical :: is_initialized + is_initialized = component_registry%is_initialized() + end function registry_is_initialized + + ! Public function to get registry size + function registry_get_size() result(size_val) + integer :: size_val + size_val = component_registry%size() + end function registry_get_size + + ! ==================== COMPONENT INFO METHODS ==================== + + subroutine ci_print(this) + class(component_info), intent(in) :: this + + if (this%order > 0) then + print *, " [", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")]" + else + print *, " [", trim(this%category), ".", trim(this%name), "]" + end if + end subroutine ci_print + + ! ==================== REGISTRY INTERNAL METHODS ==================== + + subroutine cr_register(this, info) + class(component_registry_type), intent(inout) :: this + type(component_info), intent(in) :: info + + type(component_info), allocatable :: temp(:) + integer :: i + + if (.not. this%initialized) then + error stop "[ERROR] Registry not initialized, call initialize_registry first" + end if + + ! Check if already exists + do i = 1, this%count + if (this%components(i)%category == info%category .and. & + this%components(i)%name == info%name) then + if (this%verbose) then + print *, "[WARN] Overwriting: ", & + trim(info%category), ".", trim(info%name) + end if + this%components(i) = info + return + end if + end do + + ! Expand array if needed + if (this%count >= this%capacity) then + this%capacity = this%capacity * 2 + allocate(temp(this%capacity)) + temp(1:this%count) = this%components(1:this%count) + call move_alloc(temp, this%components) + + if (this%verbose) then + print *, "[INFO] Registry expanded to capacity:", this%capacity + end if + end if + + ! Add component + this%count = this%count + 1 + this%components(this%count) = info + + if (this%verbose) then + print *, "[OK] Registered: ", trim(info%category), ".", trim(info%name) + end if + end subroutine cr_register + + function cr_get(this, category, name) result(info) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + type(component_info) :: info + + integer :: i + + ! Initialize return value as empty + info%category = "" + info%name = "" + info%order = 0 + + if (.not. this%initialized) then + return + end if + + do i = 1, this%count + if (this%components(i)%category == category .and. & + this%components(i)%name == name) then + info = this%components(i) + return + end if + end do + end function cr_get + + subroutine cr_list_all(this) + class(component_registry_type), intent(in) :: this + integer :: i + + if (.not. this%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + print *, "=== Registry Contents (", this%count, " components) ===" + + if (this%count == 0) then + print *, " (empty)" + return + end if + + ! Show components grouped by category + do i = 1, this%count + call this%components(i)%print() + end do + + print *, "===========================================" + end subroutine cr_list_all + + subroutine cr_clear(this) + class(component_registry_type), intent(inout) :: this + + if (allocated(this%components)) then + deallocate(this%components) + end if + + this%count = 0 + this%capacity = 100 + this%initialized = .false. + end subroutine cr_clear + + integer function cr_size(this) + class(component_registry_type), intent(in) :: this + cr_size = this%count + end function cr_size + + logical function cr_is_initialized(this) + class(component_registry_type), intent(in) :: this + cr_is_initialized = this%initialized + end function cr_is_initialized + + ! ==================== UTILITY FUNCTIONS ==================== + + function to_lower(str) result(lower_str) + character(len=*), intent(in) :: str + character(len=len(str)) :: lower_str + integer :: i + + do i = 1, len(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + lower_str(i:i) = char(ichar(str(i:i)) + 32) + else + lower_str(i:i) = str(i:i) + end if + end do + end function to_lower + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01i/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..46964ce7 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/src/infrastructure/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "配置基础设施模块...") + +add_library(infrastructure STATIC + config.f90 + mesh.f90 +) + +target_link_libraries(infrastructure PRIVATE core) + +target_include_directories(infrastructure PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS infrastructure + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "基础设施模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/01i/src/infrastructure/config.f90 new file mode 100644 index 00000000..34019f2f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/src/infrastructure/config.f90 @@ -0,0 +1,90 @@ +! src/infrastructure/config.f90 +module config_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, cfd_config, config_print, config_with_reconstruction + + ! CFD configuration type + type :: cfd_config + character(len=20) :: ic_type = "step" + character(len=20) :: recon_scheme = "eno" + character(len=20) :: flux_type = "rusanov" + integer :: rk_order = 1 + real(real64) :: wave_speed = 1.0_real64 + real(real64) :: final_time = 0.625_real64 + real(real64) :: dt = 0.025_real64 + character(len=20) :: boundary_type = "periodic" + real(real64) :: left_boundary_value = 1.0_real64 + real(real64) :: right_boundary_value = 2.0_real64 + integer :: spatial_order = 2 + logical :: verbose = .true. + end type cfd_config + +contains + + subroutine config_print(this) + type(cfd_config), intent(in) :: this + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(this%ic_type) + print *, "Reconstruction: ", trim(this%recon_scheme), " (order:", this%spatial_order, ")" + print *, "Flux type: ", trim(this%flux_type) + print *, "Time integration: RK", this%rk_order + print *, "Wave speed: ", this%wave_speed + print *, "Final time: ", this%final_time + print *, "Time step: ", this%dt + print *, "Boundary: ", trim(this%boundary_type) + if (trim(this%boundary_type) == 'dirichlet') then + print *, " Dirichlet values: [", this%left_boundary_value, ", ", & + this%right_boundary_value, "]" + end if + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(this, scheme, order) + type(cfd_config), intent(inout) :: this + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + character(len=20) :: scheme_lower + + ! Convert to lowercase + scheme_lower = scheme + call to_lower_inplace(scheme_lower) + this%recon_scheme = trim(adjustl(scheme_lower)) + + ! Set order + if (present(order)) then + this%spatial_order = order + else + ! Smart defaults + if (index(this%recon_scheme, 'weno') > 0) then + this%spatial_order = 5 + else if (trim(this%recon_scheme) == 'eno') then + this%spatial_order = 3 + else + print *, "[ERROR] Unsupported reconstruction scheme: ", trim(this%recon_scheme) + return + end if + end if + + if (this%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(this%recon_scheme), & + " Order: ", this%spatial_order + end if + end subroutine config_with_reconstruction + + subroutine to_lower_inplace(str) + character(len=*), intent(inout) :: str + integer :: i + + do i = 1, len_trim(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + str(i:i) = char(ichar(str(i:i)) + 32) + end if + end do + end subroutine to_lower_inplace + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/01i/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..bf1ca7eb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/src/infrastructure/mesh.f90 @@ -0,0 +1,74 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, mesh_type, mesh_init, mesh_print_info + + ! mesh类型 + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer :: ncells = 40 + integer :: nnodes + integer :: nx + real(wp), allocatable :: x(:) ! 节点坐标 + real(wp), allocatable :: xcc(:) ! 单元中心坐标 + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer, optional, intent(in) :: ncells + + integer :: i + + ! Set参数 + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! computation派生参数 + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! 分配数组 + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! 生成node坐标 + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! 生成cell中心坐标 + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== 网格信息 ===" + print *, "计算域: [", this%xmin, ", ", this%xmax, "]" + print *, "单元数: ", this%ncells + print *, "节点数: ", this%nnodes + print *, "网格尺寸 dx: ", this%dx + print *, "域长度 L: ", this%L + print *, "==========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01i/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01i/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..3fde7746 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,28 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux STATIC + base.f90 + rusanov.f90 +) + +#target_link_libraries(flux PRIVATE core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 明确设置不使用 /Qm64 选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + set_target_properties(flux PROPERTIES + Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /Qm64" # 明确设置,避免继承问题 + ) +endif() + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/01i/src/numerics/flux/base.f90 new file mode 100644 index 00000000..1ed00838 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/src/numerics/flux/base.f90 @@ -0,0 +1,23 @@ +! src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/01i/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..0600bb53 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/src/numerics/flux/rusanov.f90 @@ -0,0 +1,46 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + + interface rusanov_flux + module procedure create_rusanov_flux + end interface + +contains + + type(rusanov_flux) function create_rusanov_flux(name, wave_speed_default) result(this) + character(len=*), optional, intent(in) :: name + real(real64), optional, intent(in) :: wave_speed_default + + if (present(name)) then + this%name = name + else + this%name = "Rusanov" + end if + + if (present(wave_speed_default)) then + this%wave_speed_default = wave_speed_default + else + this%wave_speed_default = 1.0_real64 + end if + end function create_rusanov_flux + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + call flux_info(this) + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01i/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..5e4b938d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,22 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor STATIC + base.f90 + eno.f90 + weno3.f90 +) + +target_link_libraries(reconstructor PRIVATE core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/01i/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..a1ef781a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/src/numerics/reconstructor/base.f90 @@ -0,0 +1,40 @@ +! src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + procedure :: reconstruct => reconstruct_default ! 添加这个方法 + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + + ! 默认的reconstructionmethod + subroutine reconstruct_default(this, q, qL, qR) + class(reconstructor_base), intent(in) :: this + real(real64), intent(in) :: q(0:) ! 包含ghost cells + real(real64), intent(out) :: qL(:), qR(:) + integer :: i, n + + n = size(qL) + do i = 1, n + qL(i) = q(i) ! 简单的一阶重构 + qR(i) = q(i+1) + end do + end subroutine reconstruct_default +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/01i/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..62a35cf8 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,56 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + + ! Add构造函数接口 + interface eno_reconstructor + module procedure create_eno_reconstructor + end interface + +contains + + ! 构造函数 + type(eno_reconstructor) function create_eno_reconstructor(name, order, epsilon) result(this) + character(len=*), optional, intent(in) :: name + integer, optional, intent(in) :: order + real(real64), optional, intent(in) :: epsilon + + ! Set默认值 + if (present(name)) then + this%name = name + else + this%name = "ENO" + end if + + if (present(order)) then + this%order = order + else + this%order = 3 + end if + + if (present(epsilon)) then + this%epsilon = epsilon + else + this%epsilon = 1.0e-6_real64 + end if + end function create_eno_reconstructor + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + call reconstructor_info(this) + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/01i/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..c1ea9de0 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,55 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno3_reconstructor + + type, extends(reconstructor_base) :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + + ! Add构造函数接口 + interface weno3_reconstructor + module procedure create_weno3_reconstructor + end interface + +contains + + ! 构造函数 + type(weno3_reconstructor) function create_weno3_reconstructor(name, order, epsilon) result(this) + character(len=*), optional, intent(in) :: name + integer, optional, intent(in) :: order + real(real64), optional, intent(in) :: epsilon + + if (present(name)) then + this%name = name + else + this%name = "WENO3" + end if + + if (present(order)) then + this%order = order + else + this%order = 3 + end if + + if (present(epsilon)) then + this%epsilon = epsilon + else + this%epsilon = 1.0e-6_real64 + end if + end function create_weno3_reconstructor + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + call reconstructor_info(this) + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01i/tests/CMakeLists.txt new file mode 100644 index 00000000..08f6884e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/tests/CMakeLists.txt @@ -0,0 +1,29 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +add_executable(test_minimal_simple test_minimal_simple.f90) + +message(STATUS "CMAKE_Fortran_MODULE_DIRECTORY=${CMAKE_Fortran_MODULE_DIRECTORY}") + +#target_include_directories( test_minimal_simple +# PRIVATE +# ${CMAKE_Fortran_MODULE_DIRECTORY} +#) + +target_link_libraries( test_minimal_simple + PRIVATE + infrastructure +) + + +add_executable(test_factory_simple test_factory_simple.f90) + +#target_link_libraries(test_factory_simple core infrastructure reconstructor flux) + +target_link_libraries( test_factory_simple + PRIVATE + core + infrastructure + reconstructor + flux +) diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/tests/test_factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/01i/tests/test_factory.f90 new file mode 100644 index 00000000..8968b9c2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/tests/test_factory.f90 @@ -0,0 +1,154 @@ +! tests/test_factory.f90 +program test_factory + use registry_module + use config_module + use mesh_module + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + class(*), allocatable :: instance + class(reconstructor_base), allocatable :: recon + class(flux_calculator_base), allocatable :: flux_calc + real(wp), allocatable :: q(:), qL(:), qR(:), flux(:) + integer :: i, n + + print *, "=== Factory Pattern Test ===" + print *, "" + + ! Initialize systems + call initialize_registry(verbose=.true.) + + ! Register components with factories + print *, "1. Registering components with factories..." + call register_component_with_factory("reconstructor", "eno", create_eno, 3) + call register_component_with_factory("reconstructor", "weno3", create_weno3, 3) + call register_component_with_factory("flux", "rusanov", create_rusanov) + + call component_registry%list_all() + print *, "" + + ! Test creating ENO reconstructor + print *, "2. Creating ENO reconstructor..." + call create_component("reconstructor", "eno", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (eno_reconstructor) + allocate(recon, source=inst) + print *, "ENO reconstructor created successfully" + call recon%info() + print *, "" + + ! Test reconstruction + n = 10 + allocate(q(0:n+1), qL(n), qR(n)) + + ! Initialize test data (sine wave) + do i = 0, n+1 + q(i) = sin(2.0_wp * 3.141592653589793_wp * real(i-1, wp) / real(n, wp)) + end do + + print *, "Testing ENO reconstruction..." + call recon%reconstruct(q, qL, qR) + + print *, "q (internal):" + do i = 1, n + write(*, '(I3, F10.6)') i, q(i) + end do + + print *, "qL (left interface values):" + do i = 1, n + write(*, '(I3, F10.6)') i, qL(i) + end do + + deallocate(q, qL, qR) + class default + print *, "[ERROR] Wrong type for ENO reconstructor" + end select + else + print *, "[ERROR] Failed to create ENO reconstructor" + end if + print *, "" + + ! Test creating WENO3 reconstructor + print *, "3. Creating WENO3 reconstructor..." + if (allocated(instance)) deallocate(instance) + call create_component("reconstructor", "weno3", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (weno3_reconstructor) + if (allocated(recon)) deallocate(recon) + allocate(recon, source=inst) + print *, "WENO3 reconstructor created successfully" + call recon%info() + class default + print *, "[ERROR] Wrong type for WENO3 reconstructor" + end select + else + print *, "[ERROR] Failed to create WENO3 reconstructor" + end if + print *, "" + + ! Test creating Rusanov flux calculator + print *, "4. Creating Rusanov flux calculator..." + if (allocated(instance)) deallocate(instance) + call create_component("flux", "rusanov", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (rusanov_flux) + allocate(flux_calc, source=inst) + print *, "Rusanov flux calculator created successfully" + call flux_calc%info() + print *, "" + + ! Test flux computation + n = 5 + allocate(qL(n), qR(n), flux(n)) + + ! Initialize test data + do i = 1, n + qL(i) = 1.0_wp + 0.1_wp * real(i-1, wp) + qR(i) = 1.0_wp + 0.1_wp * real(i, wp) + end do + + print *, "Testing Rusanov flux computation..." + call flux_calc%compute(qL, qR, flux, 1.0_wp) + + print *, "qL:" + do i = 1, n + write(*, '(I3, F10.6)') i, qL(i) + end do + + print *, "qR:" + do i = 1, n + write(*, '(I3, F10.6)') i, qR(i) + end do + + print *, "Flux:" + do i = 1, n + write(*, '(I3, F10.6)') i, flux(i) + end do + + deallocate(qL, qR, flux) + class default + print *, "[ERROR] Wrong type for Rusanov flux" + end select + else + print *, "[ERROR] Failed to create Rusanov flux calculator" + end if + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Factory pattern test completed successfully ===" + +end program test_factory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/tests/test_factory_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/01i/tests/test_factory_simple.f90 new file mode 100644 index 00000000..7f0460eb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/tests/test_factory_simple.f90 @@ -0,0 +1,4 @@ +! tests/test_factory_simple.f90 +program test_factory_simple + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/tests/test_factory_simpleBAK.f90 b/example/1d-linear-convection/weno3/fortran/registry/01i/tests/test_factory_simpleBAK.f90 new file mode 100644 index 00000000..07a03547 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/tests/test_factory_simpleBAK.f90 @@ -0,0 +1,106 @@ +! tests/test_factory_simple.f90 +program test_factory_simple + use registry_module + use config_module + use mesh_module + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + integer :: i + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Configuration and mesh + print *, "1. Testing basic systems..." + print *, "-----------------------------" + call config_print(config) + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Creating reconstructors directly + print *, "2. Creating reconstructors..." + print *, "------------------------------" + eno = eno_reconstructor(name="ENO", order=3, epsilon=1.0e-6_wp) + weno3 = weno3_reconstructor(name="WENO3", order=3, epsilon=1.0e-6_wp) + + call eno%info() + call weno3%info() + print *, "" + + ! Test 3: Creating flux calculator directly + print *, "3. Creating flux calculator..." + print *, "-------------------------------" + rusanov = rusanov_flux(name="Rusanov", wave_speed_default=1.0_wp) + call rusanov%info() + print *, "" + + ! Test 4: Registry integration + print *, "4. Testing registry..." + print *, "----------------------" + call initialize_registry(verbose=.true.) + + ! Register with simple method + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! Check if registered + if (has_component("reconstructor", "eno")) then + print *, "[OK] ENO reconstructor registered successfully" + else + print *, "[ERROR] ENO reconstructor registration failed" + end if + + if (has_component("flux", "rusanov")) then + print *, "[OK] Rusanov flux registered successfully" + else + print *, "[ERROR] Rusanov flux registration failed" + end if + + print *, "" + call component_registry%list_all() + print *, "" + + ! Test getting available components + print *, "5. Testing component listing..." + print *, "--------------------------------" + call test_component_listing() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Factory pattern simple test completed successfully ===" + +contains + + subroutine test_component_listing() + character(len=:), allocatable :: recon_names(:) + integer, allocatable :: recon_orders(:) + integer :: i + + print *, "Available reconstructors:" + call get_available_components("reconstructor", recon_names, recon_orders) + + if (allocated(recon_names)) then + do i = 1, size(recon_names) + print *, " - ", trim(recon_names(i)) + end do + print *, "Total reconstructors: ", size(recon_names) + else + print *, " (no reconstructors found)" + end if + end subroutine test_component_listing + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/tests/test_minimal.f90 b/example/1d-linear-convection/weno3/fortran/registry/01i/tests/test_minimal.f90 new file mode 100644 index 00000000..bb4b7cda --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/tests/test_minimal.f90 @@ -0,0 +1,108 @@ +! tests/test_minimal.f90 +program test_minimal + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i ! Declare loop variable here + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", component_registry%size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components + print *, "5. Testing available components" + print *, "--------------------------------" + + ! Use a separate block to avoid allocation issues + call test_available_components() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Minimal test completed successfully ===" + +contains + + ! Internal subroutine for testing available components + subroutine test_available_components() + character(len=:), allocatable :: names(:) + integer, allocatable :: orders(:) + integer :: j + + call get_available_components("reconstructor", names, orders) + + if (allocated(names)) then + print *, "Reconstructors:" + do j = 1, size(names) + print *, " - ", trim(names(j)) + if (allocated(orders)) then + print *, " Order: ", orders(j) + end if + end do + else + print *, "No reconstructors found" + end if + end subroutine test_available_components + +end program test_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/tests/test_minimal_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/01i/tests/test_minimal_simple.f90 new file mode 100644 index 00000000..689165de --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/tests/test_minimal_simple.f90 @@ -0,0 +1,84 @@ +! tests/test_minimal_simple.f90 +program test_minimal_simple + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Simple Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components (simple version) + print *, "5. Testing registry functionality" + print *, "---------------------------------" + print *, "Registry initialized: ", registry_is_initialized() + print *, "Number of components: ", registry_get_size() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Simple test completed successfully ===" + +end program test_minimal_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01i/tests/test_minimal_simpleBAK.f90 b/example/1d-linear-convection/weno3/fortran/registry/01i/tests/test_minimal_simpleBAK.f90 new file mode 100644 index 00000000..689165de --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01i/tests/test_minimal_simpleBAK.f90 @@ -0,0 +1,84 @@ +! tests/test_minimal_simple.f90 +program test_minimal_simple + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Simple Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components (simple version) + print *, "5. Testing registry functionality" + print *, "---------------------------------" + print *, "Registry initialized: ", registry_is_initialized() + print *, "Number of components: ", registry_get_size() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Simple test completed successfully ===" + +end program test_minimal_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01j/CMakeLists.txt new file mode 100644 index 00000000..ef66d584 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/README.md b/example/1d-linear-convection/weno3/fortran/registry/01j/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/01j/scripts/build.bat new file mode 100644 index 00000000..d243695b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/scripts/build.bat @@ -0,0 +1,26 @@ +@echo off +echo ======================================== +echo Fortran CFD Project Builder +echo (Wrapper for Python script) +echo ======================================== +echo. + +REM 检查Python +where python >nul 2>nul +if %errorlevel% neq 0 ( + echo [ERROR] Python not found. Please install Python 3. + pause + exit /b 1 +) + +REM 运行Python构建脚本 +echo [INFO] Running Python build script... +python build.py %* + +if %errorlevel% neq 0 ( + echo [ERROR] Build failed + pause + exit /b 1 +) + +pause \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/scripts/build.ps1 b/example/1d-linear-convection/weno3/fortran/registry/01j/scripts/build.ps1 new file mode 100644 index 00000000..4497c5eb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/scripts/build.ps1 @@ -0,0 +1,32 @@ +# Fortran CFD Project Builder (PowerShell wrapper) + +Write-Host "========================================" -ForegroundColor Cyan +Write-Host " Fortran CFD Project Builder" -ForegroundColor Cyan +Write-Host " (PowerShell wrapper for Python script)" -ForegroundColor Cyan +Write-Host "========================================" -ForegroundColor Cyan +Write-Host "" + +# 检查Python +$python = Get-Command python -ErrorAction SilentlyContinue +if (-not $python) { + $python = Get-Command python3 -ErrorAction SilentlyContinue +} + +if (-not $python) { + Write-Host "[ERROR] Python not found. Please install Python 3." -ForegroundColor Red + Read-Host "Press Enter to exit" + exit 1 +} + +# 运行Python构建脚本 +Write-Host "[INFO] Running Python build script..." -ForegroundColor Yellow +$argsString = $args -join ' ' +$command = "python build.py $argsString" + +Invoke-Expression $command + +if ($LASTEXITCODE -ne 0) { + Write-Host "[ERROR] Build failed" -ForegroundColor Red + Read-Host "Press Enter to exit" + exit 1 +} \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/01j/scripts/build.py new file mode 100644 index 00000000..1c99c2c8 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/scripts/build.py @@ -0,0 +1,357 @@ +#!/usr/bin/env python3 +""" +Fortran CFD 项目构建脚本 (Python版) +支持 Intel oneAPI 环境的自动化构建 +""" + +import os +import sys +import subprocess +import shutil +from pathlib import Path +import argparse +import platform +import time + +class Colors: + """终端颜色""" + if platform.system() == "Windows": + # Windows 启用 ANSI + os.system("") + + HEADER = '\033[95m' + BLUE = '\033[94m' + CYAN = '\033[96m' + GREEN = '\033[92m' + YELLOW = '\033[93m' + RED = '\033[91m' + MAGENTA = '\033[95m' + DARK_GRAY = '\033[90m' # 添加这个 + END = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + +def print_color(text, color=Colors.END): + """彩色打印""" + print(f"{color}{text}{Colors.END}") + +def print_header(text): + """打印标题""" + print_color("\n" + "="*60, Colors.CYAN) + print_color(f" {text}", Colors.BOLD + Colors.CYAN) + print_color("="*60 + "\n", Colors.CYAN) + +def print_step(step, total, message): + """打印步骤""" + print_color(f"[{step}/{total}] {message}...", Colors.YELLOW) + +def print_success(message): + """打印成功""" + print_color(f"✓ {message}", Colors.GREEN) + +def print_error(message): + """打印错误""" + print_color(f"✗ {message}", Colors.RED) + +def print_warning(message): + """打印警告""" + print_color(f"! {message}", Colors.YELLOW) + +def run_command(cmd, cwd=None, check=True, capture=True): + """运行命令""" + print_color(f" $ {' '.join(cmd)}", Colors.BLUE) + + try: + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=capture, + text=True, + encoding='utf-8', + errors='ignore', + shell=False + ) + + if capture and result.stdout: + print(result.stdout) + if capture and result.stderr: + print_color(result.stderr, Colors.YELLOW) + + if check and result.returncode != 0: + print_error(f"Command failed with exit code: {result.returncode}") + return False + + return True + + except FileNotFoundError as e: + print_error(f"Command not found: {cmd[0]}") + if check: + raise + return False + except Exception as e: + print_error(f"Command execution failed: {e}") + return False + +def setup_intel_environment(): + """设置 Intel oneAPI 环境""" + setvars_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + ] + + setvars_path = None + for path in setvars_paths: + if os.path.exists(path): + setvars_path = path + break + + if not setvars_path: + print_error("Intel oneAPI setvars.bat not found.") + print_warning("Please install Intel oneAPI or update the path in build.py") + return False + + # 创建临时的 batch 文件 + temp_bat = "temp_setvars.bat" + with open(temp_bat, 'w') as f: + f.write(f'@echo off\n') + f.write(f'call "{setvars_path}" > nul 2>&1\n') + f.write(f'set\n') + + try: + # 运行并捕获环境变量 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + shell=True, + encoding='utf-8', + errors='ignore' + ) + + # 解析并设置环境变量 + for line in result.stdout.split('\n'): + if '=' in line: + key, value = line.split('=', 1) + os.environ[key.strip()] = value.strip() + + os.remove(temp_bat) + print_success("Intel oneAPI environment configured") + return True + + except Exception as e: + print_error(f"Failed to setup Intel environment: {e}") + if os.path.exists(temp_bat): + os.remove(temp_bat) + return False + +def build_project(args): + """构建项目主函数""" + start_time = time.time() + + print_header(f"Fortran CFD Project Builder") + print_color(f"Build type: {args.build_type}", Colors.CYAN) + print_color(f"Run tests: {args.run_tests}", Colors.CYAN) + print() + + # 获取项目根目录(脚本所在目录的父目录) + script_dir = Path(__file__).parent + project_root = script_dir.parent + os.chdir(project_root) + + print_color(f"Project root: {project_root}", Colors.BLUE) + + # 步骤1: 设置 Intel 环境 + print_step(1, 4, "Setting up Intel Fortran compiler") + if not setup_intel_environment(): + return False + + # 步骤2: 准备构建目录 + print_step(2, 4, "Preparing build directory") + build_dir = project_root / "build" + + if args.clean and build_dir.exists(): + try: + shutil.rmtree(build_dir) + print_success("Cleaned build directory") + except Exception as e: + print_error(f"Failed to clean build directory: {e}") + if not args.force: + return False + + build_dir.mkdir(exist_ok=True) + os.chdir(build_dir) + + # 步骤3: 配置项目 + print_step(3, 4, "Configuring project") + + cmake_cmd = [ + "cmake", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-T", "fortran=ifx", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ".." + ] + + if not run_command(cmake_cmd, check=not args.force): + if not args.force: + return False + + # 步骤4: 构建项目 + print_step(4, 4, "Building project") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + f"-j{args.jobs}" if args.jobs > 1 else "" + ] + + print(f"build_cmd={build_cmd}") + build_cmd = [c for c in build_cmd if c] # 移除空字符串 + + if not run_command(build_cmd, check=not args.force): + if not args.force: + return False + + build_time = time.time() - start_time + print_success(f"Build completed in {build_time:.1f} seconds") + + # 运行测试 + if args.run_tests: + print_header("Running Tests") + run_tests(args.build_type) + + print_header("Build Summary") + print_color(f"Build directory: {build_dir}", Colors.GREEN) + print_color(f"Build type: {args.build_type}", Colors.GREEN) + print_color(f"Total time: {build_time:.1f}s", Colors.GREEN) + + return True + +def run_tests(build_type): + """运行测试""" + tests = [ + ("test_simple", "Simple functionality test"), + ("test_factory", "Factory pattern test"), + ] + + for test_name, description in tests: + # 修复:在这里直接访问当前循环的 test_name + _run_single_test(test_name, description, build_type) + +def _run_single_test(test_name, description, build_type): + """运行单个测试(修复作用域问题)""" + # 可能的测试可执行文件路径 + possible_paths = [ + Path(f"./{build_type}/{test_name}.exe"), + Path(f"./tests/{build_type}/{test_name}.exe"), + Path(f"./tests/{test_name}.exe"), + Path(f"{test_name}.exe"), + Path(f"./{test_name}.exe"), + ] + + test_exe = None + + # 尝试所有可能的路径 + for path in possible_paths: + if path.exists(): + test_exe = path + break + + if test_exe: + print_color(f"\n[TEST] {description}...", Colors.MAGENTA) + print_color("-" * 50, Colors.DARK_GRAY) + + try: + result = subprocess.run( + [str(test_exe)], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + + if result.stdout: + print(result.stdout) + + if result.returncode == 0: + print_success(f"{test_name} passed") + else: + print_error(f"{test_name} failed (exit code: {result.returncode})") + if result.stderr: + print_color(result.stderr, Colors.YELLOW) + + except Exception as e: + print_error(f"{test_name} failed: {e}") + else: + print_warning(f"{test_name}.exe not found. Searched paths:") + for path in possible_paths: + print_color(f" - {path}", Colors.YELLOW) + +def main(): + """主函数""" + parser = argparse.ArgumentParser(description="Fortran CFD Project Builder") + + parser.add_argument( + "--build-type", + choices=["Debug", "Release", "RelWithDebInfo", "MinSizeRel"], + default="Debug", + help="Build type (default: Debug)" + ) + + parser.add_argument( + "--clean", + action="store_true", + help="Clean build directory before building" + ) + + parser.add_argument( + "--no-tests", + action="store_true", + help="Skip running tests" + ) + + parser.add_argument( + "-j", "--jobs", + type=int, + default=0, + help="Number of parallel jobs (0 = use all cores)" + ) + + parser.add_argument( + "--force", + action="store_true", + help="Continue on errors" + ) + + parser.add_argument( + "--verbose", + action="store_true", + help="Verbose output" + ) + + args = parser.parse_args() + + if args.jobs == 0: + import multiprocessing + args.jobs = multiprocessing.cpu_count() + + args.run_tests = not args.no_tests + + try: + success = build_project(args) + if not success and not args.force: + sys.exit(1) + except KeyboardInterrupt: + print_color("\nBuild interrupted by user", Colors.YELLOW) + sys.exit(1) + except Exception as e: + print_error(f"Unexpected error: {e}") + if args.verbose: + import traceback + traceback.print_exc() + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/01j/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01j/src/CMakeLists.txt new file mode 100644 index 00000000..ee38952b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/src/CMakeLists.txt @@ -0,0 +1,14 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01j/src/core/CMakeLists.txt new file mode 100644 index 00000000..bcfd024d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/src/core/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/core/CMakeLists.txt +message(STATUS "配置核心模块...") + +add_library(core STATIC + registry.f90 + factory_interfaces.f90 +) + +target_include_directories(core PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 安装规则 +install(TARGETS core + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) +install(FILES + ${CMAKE_Fortran_MODULE_DIRECTORY}/registry_module.mod + ${CMAKE_Fortran_MODULE_DIRECTORY}/factory_interfaces.mod + DESTINATION include/fortran_cfd/core +) + +message(STATUS "核心模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/01j/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..a960167c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/01j/src/core/registry.f90 new file mode 100644 index 00000000..bf9028c3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/src/core/registry.f90 @@ -0,0 +1,318 @@ +! src/core/registry.f90 +module registry_module + use, intrinsic :: iso_fortran_env, only: real64 + use factory_interfaces, only: factory_procedure + implicit none + + private + + ! Public interface + public :: real64, component_info, component_registry + public :: register_component_simple, initialize_registry, cleanup_registry + public :: has_component, get_available_components + public :: registry_is_initialized, registry_get_size ! + + ! Type definitions (simplified, no factory for now) + type :: component_info + character(len=32) :: category = "" + character(len=32) :: name = "" + integer :: order = 0 + contains + procedure :: print => ci_print + end type component_info + + type :: component_registry_type + private + type(component_info), allocatable :: components(:) + integer :: count = 0 + integer :: capacity = 100 + logical :: verbose = .true. + logical :: initialized = .false. + contains + procedure :: register => cr_register + procedure :: get => cr_get + procedure :: list_all => cr_list_all + procedure :: clear => cr_clear + procedure :: size => cr_size + procedure :: is_initialized => cr_is_initialized ! + end type component_registry_type + + ! Global registry instance + type(component_registry_type), save :: component_registry + +contains + + ! ==================== PUBLIC API ==================== + + ! Initialize registry + subroutine initialize_registry(initial_capacity, verbose) + integer, optional, intent(in) :: initial_capacity + logical, optional, intent(in) :: verbose + + if (component_registry%initialized) then + if (component_registry%verbose) then + print *, "[INFO] Registry already initialized" + end if + return + end if + + if (present(initial_capacity)) then + component_registry%capacity = max(10, initial_capacity) + end if + + if (present(verbose)) then + component_registry%verbose = verbose + end if + + ! Allocate array + allocate(component_registry%components(component_registry%capacity)) + + component_registry%initialized = .true. + component_registry%count = 0 + + if (component_registry%verbose) then + print *, "[INIT] Registry initialized, capacity:", component_registry%capacity + end if + end subroutine initialize_registry + + ! Cleanup registry + subroutine cleanup_registry + call component_registry%clear() + if (component_registry%verbose) then + print *, "[CLEANUP] Registry cleaned up" + end if + end subroutine cleanup_registry + + ! Simple registration (no factory) + subroutine register_component_simple(category, name) + character(len=*), intent(in) :: category, name + + type(component_info) :: info + + info%category = to_lower(trim(adjustl(category))) + info%name = to_lower(trim(adjustl(name))) + info%order = 0 + + call component_registry%register(info) + end subroutine register_component_simple + + ! Check if component exists + function has_component(category, name) result(found) + character(len=*), intent(in) :: category, name + logical :: found + + type(component_info) :: info + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + info = component_registry%get(cat_lower, name_lower) + found = (len_trim(info%category) > 0) + end function has_component + + ! Get available components in a category + subroutine get_available_components(category, names, orders) + character(len=*), intent(in) :: category + character(len=:), allocatable, intent(out), optional :: names(:) + integer, allocatable, intent(out), optional :: orders(:) + + character(len=32) :: cat_lower + integer :: i, count, idx + type(component_info) :: info + + cat_lower = to_lower(trim(adjustl(category))) + + ! Count components in this category + count = 0 + do i = 1, component_registry%count + if (component_registry%components(i)%category == cat_lower) then + count = count + 1 + end if + end do + + ! Allocate arrays if requested + if (present(names)) then + allocate(character(len=32) :: names(count)) + end if + + if (present(orders)) then + allocate(orders(count)) + end if + + ! Fill arrays + idx = 1 + do i = 1, component_registry%count + if (component_registry%components(i)%category == cat_lower) then + info = component_registry%components(i) + if (present(names)) then + names(idx) = info%name + end if + if (present(orders)) then + orders(idx) = info%order + end if + idx = idx + 1 + end if + end do + end subroutine get_available_components + + ! Public function to check if registry is initialized + function registry_is_initialized() result(is_initialized) + logical :: is_initialized + is_initialized = component_registry%is_initialized() + end function registry_is_initialized + + ! Public function to get registry size + function registry_get_size() result(size_val) + integer :: size_val + size_val = component_registry%size() + end function registry_get_size + + ! ==================== COMPONENT INFO METHODS ==================== + + subroutine ci_print(this) + class(component_info), intent(in) :: this + + if (this%order > 0) then + print *, " [", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")]" + else + print *, " [", trim(this%category), ".", trim(this%name), "]" + end if + end subroutine ci_print + + ! ==================== REGISTRY INTERNAL METHODS ==================== + + subroutine cr_register(this, info) + class(component_registry_type), intent(inout) :: this + type(component_info), intent(in) :: info + + type(component_info), allocatable :: temp(:) + integer :: i + + if (.not. this%initialized) then + error stop "[ERROR] Registry not initialized, call initialize_registry first" + end if + + ! Check if already exists + do i = 1, this%count + if (this%components(i)%category == info%category .and. & + this%components(i)%name == info%name) then + if (this%verbose) then + print *, "[WARN] Overwriting: ", & + trim(info%category), ".", trim(info%name) + end if + this%components(i) = info + return + end if + end do + + ! Expand array if needed + if (this%count >= this%capacity) then + this%capacity = this%capacity * 2 + allocate(temp(this%capacity)) + temp(1:this%count) = this%components(1:this%count) + call move_alloc(temp, this%components) + + if (this%verbose) then + print *, "[INFO] Registry expanded to capacity:", this%capacity + end if + end if + + ! Add component + this%count = this%count + 1 + this%components(this%count) = info + + if (this%verbose) then + print *, "[OK] Registered: ", trim(info%category), ".", trim(info%name) + end if + end subroutine cr_register + + function cr_get(this, category, name) result(info) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + type(component_info) :: info + + integer :: i + + ! Initialize return value as empty + info%category = "" + info%name = "" + info%order = 0 + + if (.not. this%initialized) then + return + end if + + do i = 1, this%count + if (this%components(i)%category == category .and. & + this%components(i)%name == name) then + info = this%components(i) + return + end if + end do + end function cr_get + + subroutine cr_list_all(this) + class(component_registry_type), intent(in) :: this + integer :: i + + if (.not. this%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + print *, "=== Registry Contents (", this%count, " components) ===" + + if (this%count == 0) then + print *, " (empty)" + return + end if + + ! Show components grouped by category + do i = 1, this%count + call this%components(i)%print() + end do + + print *, "===========================================" + end subroutine cr_list_all + + subroutine cr_clear(this) + class(component_registry_type), intent(inout) :: this + + if (allocated(this%components)) then + deallocate(this%components) + end if + + this%count = 0 + this%capacity = 100 + this%initialized = .false. + end subroutine cr_clear + + integer function cr_size(this) + class(component_registry_type), intent(in) :: this + cr_size = this%count + end function cr_size + + logical function cr_is_initialized(this) + class(component_registry_type), intent(in) :: this + cr_is_initialized = this%initialized + end function cr_is_initialized + + ! ==================== UTILITY FUNCTIONS ==================== + + function to_lower(str) result(lower_str) + character(len=*), intent(in) :: str + character(len=len(str)) :: lower_str + integer :: i + + do i = 1, len(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + lower_str(i:i) = char(ichar(str(i:i)) + 32) + else + lower_str(i:i) = str(i:i) + end if + end do + end function to_lower + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01j/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..46964ce7 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/src/infrastructure/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "配置基础设施模块...") + +add_library(infrastructure STATIC + config.f90 + mesh.f90 +) + +target_link_libraries(infrastructure PRIVATE core) + +target_include_directories(infrastructure PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS infrastructure + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "基础设施模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/01j/src/infrastructure/config.f90 new file mode 100644 index 00000000..d3b1e0df --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/src/infrastructure/config.f90 @@ -0,0 +1,90 @@ +! src/infrastructure/config.f90 +module config_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, cfd_config, config_print, config_with_reconstruction + + ! CFD configuration type + type :: cfd_config + character(len=20) :: ic_type = "step" + character(len=20) :: recon_scheme = "eno" + character(len=20) :: flux_type = "rusanov" + integer :: rk_order = 1 + real(real64) :: wave_speed = 1.0_real64 + real(real64) :: final_time = 0.625_real64 + real(real64) :: dt = 0.025_real64 + character(len=20) :: boundary_type = "periodic" + real(real64) :: left_boundary_value = 1.0_real64 + real(real64) :: right_boundary_value = 2.0_real64 + integer :: spatial_order = 2 + logical :: verbose = .true. + end type cfd_config + +contains + + subroutine config_print(this) + type(cfd_config), intent(in) :: this + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(this%ic_type) + print *, "Reconstruction: ", trim(this%recon_scheme), " (order:", this%spatial_order, ")" + print *, "Flux type: ", trim(this%flux_type) + print *, "Time integration: RK", this%rk_order + print *, "Wave speed: ", this%wave_speed + print *, "Final time: ", this%final_time + print *, "Time step: ", this%dt + print *, "Boundary: ", trim(this%boundary_type) + if (trim(this%boundary_type) == 'dirichlet') then + print *, " Dirichlet values: [", this%left_boundary_value, ", ", & + this%right_boundary_value, "]" + end if + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(this, scheme, order) + type(cfd_config), intent(inout) :: this + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + character(len=20) :: scheme_lower + + ! Convert to lowercase + scheme_lower = scheme + call to_lower_inplace(scheme_lower) + this%recon_scheme = trim(adjustl(scheme_lower)) + + ! Set order + if (present(order)) then + this%spatial_order = order + else + ! Smart defaults + if (index(this%recon_scheme, 'weno') > 0) then + this%spatial_order = 5 + else if (trim(this%recon_scheme) == 'eno') then + this%spatial_order = 3 + else + print *, "[ERROR] Unsupported reconstruction scheme: ", trim(this%recon_scheme) + return + end if + end if + + if (this%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(this%recon_scheme), & + " Order: ", this%spatial_order + end if + end subroutine config_with_reconstruction + + subroutine to_lower_inplace(str) + character(len=*), intent(inout) :: str + integer :: i + + do i = 1, len_trim(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + str(i:i) = char(ichar(str(i:i)) + 32) + end if + end do + end subroutine to_lower_inplace + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/01j/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..896969bd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/src/infrastructure/mesh.f90 @@ -0,0 +1,74 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, mesh_type, mesh_init, mesh_print_info + + ! mesh + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer :: ncells = 40 + integer :: nnodes + integer :: nx + real(wp), allocatable :: x(:) ! + real(wp), allocatable :: xcc(:) ! + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer, optional, intent(in) :: ncells + + integer :: i + + ! Set + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! computation + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! node + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! cell + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== ===" + print *, ": [", this%xmin, ", ", this%xmax, "]" + print *, ": ", this%ncells + print *, ": ", this%nnodes + print *, " dx: ", this%dx + print *, " L: ", this%L + print *, "==========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01j/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01j/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..3fde7746 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,28 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux STATIC + base.f90 + rusanov.f90 +) + +#target_link_libraries(flux PRIVATE core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 明确设置不使用 /Qm64 选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + set_target_properties(flux PROPERTIES + Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /Qm64" # 明确设置,避免继承问题 + ) +endif() + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/01j/src/numerics/flux/base.f90 new file mode 100644 index 00000000..87439627 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/src/numerics/flux/base.f90 @@ -0,0 +1,23 @@ +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info ! 这里定义了接口 + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/01j/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..859b6ed2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/src/numerics/flux/rusanov.f90 @@ -0,0 +1,37 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + + ! 添加构造函数接口 + interface rusanov_flux + module procedure create_rusanov_flux + end interface + +contains + + ! 构造函数 + type(rusanov_flux) function create_rusanov_flux() result(this) + this%name = "Rusanov" + this%wave_speed_default = 1.0_real64 + end function create_rusanov_flux + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + ! 必须调用父类的info方法 + call flux_info(this) ! 这会调用base模块中的flux_info + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01j/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..5e4b938d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,22 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor STATIC + base.f90 + eno.f90 + weno3.f90 +) + +target_link_libraries(reconstructor PRIVATE core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/01j/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..8c337286 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/src/numerics/reconstructor/base.f90 @@ -0,0 +1,26 @@ +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info ! 这里定义了接口 + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/01j/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..fe54d5c3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,38 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + + ! 必须添加构造函数接口 + interface eno_reconstructor + module procedure create_eno_reconstructor + end interface + +contains + + ! 构造函数实现 + type(eno_reconstructor) function create_eno_reconstructor() result(this) + this%name = "ENO" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_eno_reconstructor + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + ! 必须调用父类的info方法 + call reconstructor_info(this) ! 这会调用base模块中的reconstructor_info + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/01j/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..a1f2a329 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,55 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno3_reconstructor + + type, extends(reconstructor_base) :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + + ! Add + interface weno3_reconstructor + module procedure create_weno3_reconstructor + end interface + +contains + + ! + type(weno3_reconstructor) function create_weno3_reconstructor(name, order, epsilon) result(this) + character(len=*), optional, intent(in) :: name + integer, optional, intent(in) :: order + real(real64), optional, intent(in) :: epsilon + + if (present(name)) then + this%name = name + else + this%name = "WENO3" + end if + + if (present(order)) then + this%order = order + else + this%order = 3 + end if + + if (present(epsilon)) then + this%epsilon = epsilon + else + this%epsilon = 1.0e-6_real64 + end if + end function create_weno3_reconstructor + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + call reconstructor_info(this) + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01j/tests/CMakeLists.txt new file mode 100644 index 00000000..ac77721e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/tests/CMakeLists.txt @@ -0,0 +1,34 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +add_executable(test_minimal_simple test_minimal_simple.f90) + +message(STATUS "CMAKE_Fortran_MODULE_DIRECTORY=${CMAKE_Fortran_MODULE_DIRECTORY}") + +#target_include_directories( test_minimal_simple +# PRIVATE +# ${CMAKE_Fortran_MODULE_DIRECTORY} +#) + +target_link_libraries( test_minimal_simple + PRIVATE + infrastructure +) + +add_executable(test_simple_link test_simple_link.f90) +target_link_libraries(test_simple_link + PRIVATE + reconstructor + flux +) + + +#add_executable(test_factory_simple test_factory_simple.f90) + +#target_link_libraries( test_factory_simple +# PRIVATE +# core +# infrastructure +# reconstructor +# flux +#) diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/tests/test_factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/01j/tests/test_factory.f90 new file mode 100644 index 00000000..c62d5f8a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/tests/test_factory.f90 @@ -0,0 +1,154 @@ +! tests/test_factory.f90 +program test_factory + use registry_module + use config_module + use mesh_module + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + class(*), allocatable :: instance + class(reconstructor_base), allocatable :: recon + class(flux_calculator_base), allocatable :: flux_calc + real(wp), allocatable :: q(:), qL(:), qR(:), flux(:) + integer :: i, n + + print *, "=== Factory Pattern Test ===" + print *, "" + + ! Initialize systems + call initialize_registry(verbose=.true.) + + ! Register components with factories + print *, "1. Registering components with factories..." + call register_component_with_factory("reconstructor", "eno", create_eno, 3) + call register_component_with_factory("reconstructor", "weno3", create_weno3, 3) + call register_component_with_factory("flux", "rusanov", create_rusanov) + + call component_registry%list_all() + print *, "" + + ! Test creating ENO reconstructor + print *, "2. Creating ENO reconstructor..." + call create_component("reconstructor", "eno", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (eno_reconstructor) + allocate(recon, source=inst) + print *, "ENO reconstructor created successfully" + call recon%info() + print *, "" + + ! Test reconstruction + n = 10 + allocate(q(0:n+1), qL(n), qR(n)) + + ! Initialize test data (sine wave) + do i = 0, n+1 + q(i) = sin(2.0_wp * 3.141592653589793_wp * real(i-1, wp) / real(n, wp)) + end do + + print *, "Testing ENO reconstruction..." + call recon%reconstruct(q, qL, qR) + + print *, "q (internal):" + do i = 1, n + write(*, '(I3, F10.6)') i, q(i) + end do + + print *, "qL (left interface values):" + do i = 1, n + write(*, '(I3, F10.6)') i, qL(i) + end do + + deallocate(q, qL, qR) + class default + print *, "[ERROR] Wrong type for ENO reconstructor" + end select + else + print *, "[ERROR] Failed to create ENO reconstructor" + end if + print *, "" + + ! Test creating WENO3 reconstructor + print *, "3. Creating WENO3 reconstructor..." + if (allocated(instance)) deallocate(instance) + call create_component("reconstructor", "weno3", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (weno3_reconstructor) + if (allocated(recon)) deallocate(recon) + allocate(recon, source=inst) + print *, "WENO3 reconstructor created successfully" + call recon%info() + class default + print *, "[ERROR] Wrong type for WENO3 reconstructor" + end select + else + print *, "[ERROR] Failed to create WENO3 reconstructor" + end if + print *, "" + + ! Test creating Rusanov flux calculator + print *, "4. Creating Rusanov flux calculator..." + if (allocated(instance)) deallocate(instance) + call create_component("flux", "rusanov", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (rusanov_flux) + allocate(flux_calc, source=inst) + print *, "Rusanov flux calculator created successfully" + call flux_calc%info() + print *, "" + + ! Test flux computation + n = 5 + allocate(qL(n), qR(n), flux(n)) + + ! Initialize test data + do i = 1, n + qL(i) = 1.0_wp + 0.1_wp * real(i-1, wp) + qR(i) = 1.0_wp + 0.1_wp * real(i, wp) + end do + + print *, "Testing Rusanov flux computation..." + call flux_calc%compute(qL, qR, flux, 1.0_wp) + + print *, "qL:" + do i = 1, n + write(*, '(I3, F10.6)') i, qL(i) + end do + + print *, "qR:" + do i = 1, n + write(*, '(I3, F10.6)') i, qR(i) + end do + + print *, "Flux:" + do i = 1, n + write(*, '(I3, F10.6)') i, flux(i) + end do + + deallocate(qL, qR, flux) + class default + print *, "[ERROR] Wrong type for Rusanov flux" + end select + else + print *, "[ERROR] Failed to create Rusanov flux calculator" + end if + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Factory pattern test completed successfully ===" + +end program test_factory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/tests/test_factory_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/01j/tests/test_factory_simple.f90 new file mode 100644 index 00000000..2a4650d4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/tests/test_factory_simple.f90 @@ -0,0 +1,72 @@ +program test_factory_simple + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module, only: initialize_registry, cleanup_registry, & + register_component_simple, has_component + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Basic systems + print *, "1. Testing basic systems..." + call config_print(config) + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Reconstructors - 必须调用info方法 + print *, "2. Testing reconstructors..." + print *, "Creating ENO reconstructor..." + + ! 创建对象 + eno = eno_reconstructor() + + ! 必须调用info方法,否则链接器会认为不需要这些符号 + call eno%info() + + print *, "Creating WENO3 reconstructor..." + weno3 = weno3_reconstructor() + call weno3%info() + print *, "" + + ! Test 3: Flux calculator + print *, "3. Testing flux calculator..." + rusanov = rusanov_flux() + call rusanov%info() + print *, "" + + ! Test 4: Registry + print *, "4. Testing registry..." + call initialize_registry(verbose=.true.) + + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + if (has_component("reconstructor", "eno")) then + print *, "[OK] ENO reconstructor registered successfully" + end if + + if (has_component("flux", "rusanov")) then + print *, "[OK] Rusanov flux registered successfully" + end if + + print *, "" + call cleanup_registry() + + print *, "=== Factory pattern simple test completed successfully ===" + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/tests/test_factory_simpleBAK.f90 b/example/1d-linear-convection/weno3/fortran/registry/01j/tests/test_factory_simpleBAK.f90 new file mode 100644 index 00000000..07a03547 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/tests/test_factory_simpleBAK.f90 @@ -0,0 +1,106 @@ +! tests/test_factory_simple.f90 +program test_factory_simple + use registry_module + use config_module + use mesh_module + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + integer :: i + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Configuration and mesh + print *, "1. Testing basic systems..." + print *, "-----------------------------" + call config_print(config) + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Creating reconstructors directly + print *, "2. Creating reconstructors..." + print *, "------------------------------" + eno = eno_reconstructor(name="ENO", order=3, epsilon=1.0e-6_wp) + weno3 = weno3_reconstructor(name="WENO3", order=3, epsilon=1.0e-6_wp) + + call eno%info() + call weno3%info() + print *, "" + + ! Test 3: Creating flux calculator directly + print *, "3. Creating flux calculator..." + print *, "-------------------------------" + rusanov = rusanov_flux(name="Rusanov", wave_speed_default=1.0_wp) + call rusanov%info() + print *, "" + + ! Test 4: Registry integration + print *, "4. Testing registry..." + print *, "----------------------" + call initialize_registry(verbose=.true.) + + ! Register with simple method + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! Check if registered + if (has_component("reconstructor", "eno")) then + print *, "[OK] ENO reconstructor registered successfully" + else + print *, "[ERROR] ENO reconstructor registration failed" + end if + + if (has_component("flux", "rusanov")) then + print *, "[OK] Rusanov flux registered successfully" + else + print *, "[ERROR] Rusanov flux registration failed" + end if + + print *, "" + call component_registry%list_all() + print *, "" + + ! Test getting available components + print *, "5. Testing component listing..." + print *, "--------------------------------" + call test_component_listing() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Factory pattern simple test completed successfully ===" + +contains + + subroutine test_component_listing() + character(len=:), allocatable :: recon_names(:) + integer, allocatable :: recon_orders(:) + integer :: i + + print *, "Available reconstructors:" + call get_available_components("reconstructor", recon_names, recon_orders) + + if (allocated(recon_names)) then + do i = 1, size(recon_names) + print *, " - ", trim(recon_names(i)) + end do + print *, "Total reconstructors: ", size(recon_names) + else + print *, " (no reconstructors found)" + end if + end subroutine test_component_listing + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/tests/test_minimal.f90 b/example/1d-linear-convection/weno3/fortran/registry/01j/tests/test_minimal.f90 new file mode 100644 index 00000000..807d0bc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/tests/test_minimal.f90 @@ -0,0 +1,108 @@ +! tests/test_minimal.f90 +program test_minimal + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i ! Declare loop variable here + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", component_registry%size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components + print *, "5. Testing available components" + print *, "--------------------------------" + + ! Use a separate block to avoid allocation issues + call test_available_components() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Minimal test completed successfully ===" + +contains + + ! Internal subroutine for testing available components + subroutine test_available_components() + character(len=:), allocatable :: names(:) + integer, allocatable :: orders(:) + integer :: j + + call get_available_components("reconstructor", names, orders) + + if (allocated(names)) then + print *, "Reconstructors:" + do j = 1, size(names) + print *, " - ", trim(names(j)) + if (allocated(orders)) then + print *, " Order: ", orders(j) + end if + end do + else + print *, "No reconstructors found" + end if + end subroutine test_available_components + +end program test_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/tests/test_minimal_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/01j/tests/test_minimal_simple.f90 new file mode 100644 index 00000000..6213e8f8 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/tests/test_minimal_simple.f90 @@ -0,0 +1,84 @@ +! tests/test_minimal_simple.f90 +program test_minimal_simple + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Simple Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components (simple version) + print *, "5. Testing registry functionality" + print *, "---------------------------------" + print *, "Registry initialized: ", registry_is_initialized() + print *, "Number of components: ", registry_get_size() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Simple test completed successfully ===" + +end program test_minimal_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/tests/test_minimal_simpleBAK.f90 b/example/1d-linear-convection/weno3/fortran/registry/01j/tests/test_minimal_simpleBAK.f90 new file mode 100644 index 00000000..6213e8f8 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/tests/test_minimal_simpleBAK.f90 @@ -0,0 +1,84 @@ +! tests/test_minimal_simple.f90 +program test_minimal_simple + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Simple Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components (simple version) + print *, "5. Testing registry functionality" + print *, "---------------------------------" + print *, "Registry initialized: ", registry_is_initialized() + print *, "Number of components: ", registry_get_size() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Simple test completed successfully ===" + +end program test_minimal_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01j/tests/test_simple_link.f90 b/example/1d-linear-convection/weno3/fortran/registry/01j/tests/test_simple_link.f90 new file mode 100644 index 00000000..807d0bc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01j/tests/test_simple_link.f90 @@ -0,0 +1,108 @@ +! tests/test_minimal.f90 +program test_minimal + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i ! Declare loop variable here + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", component_registry%size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components + print *, "5. Testing available components" + print *, "--------------------------------" + + ! Use a separate block to avoid allocation issues + call test_available_components() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Minimal test completed successfully ===" + +contains + + ! Internal subroutine for testing available components + subroutine test_available_components() + character(len=:), allocatable :: names(:) + integer, allocatable :: orders(:) + integer :: j + + call get_available_components("reconstructor", names, orders) + + if (allocated(names)) then + print *, "Reconstructors:" + do j = 1, size(names) + print *, " - ", trim(names(j)) + if (allocated(orders)) then + print *, " Order: ", orders(j) + end if + end do + else + print *, "No reconstructors found" + end if + end subroutine test_available_components + +end program test_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01k/CMakeLists.txt new file mode 100644 index 00000000..ef66d584 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/README.md b/example/1d-linear-convection/weno3/fortran/registry/01k/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/01k/scripts/build.bat new file mode 100644 index 00000000..d243695b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/scripts/build.bat @@ -0,0 +1,26 @@ +@echo off +echo ======================================== +echo Fortran CFD Project Builder +echo (Wrapper for Python script) +echo ======================================== +echo. + +REM 检查Python +where python >nul 2>nul +if %errorlevel% neq 0 ( + echo [ERROR] Python not found. Please install Python 3. + pause + exit /b 1 +) + +REM 运行Python构建脚本 +echo [INFO] Running Python build script... +python build.py %* + +if %errorlevel% neq 0 ( + echo [ERROR] Build failed + pause + exit /b 1 +) + +pause \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/scripts/build.ps1 b/example/1d-linear-convection/weno3/fortran/registry/01k/scripts/build.ps1 new file mode 100644 index 00000000..4497c5eb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/scripts/build.ps1 @@ -0,0 +1,32 @@ +# Fortran CFD Project Builder (PowerShell wrapper) + +Write-Host "========================================" -ForegroundColor Cyan +Write-Host " Fortran CFD Project Builder" -ForegroundColor Cyan +Write-Host " (PowerShell wrapper for Python script)" -ForegroundColor Cyan +Write-Host "========================================" -ForegroundColor Cyan +Write-Host "" + +# 检查Python +$python = Get-Command python -ErrorAction SilentlyContinue +if (-not $python) { + $python = Get-Command python3 -ErrorAction SilentlyContinue +} + +if (-not $python) { + Write-Host "[ERROR] Python not found. Please install Python 3." -ForegroundColor Red + Read-Host "Press Enter to exit" + exit 1 +} + +# 运行Python构建脚本 +Write-Host "[INFO] Running Python build script..." -ForegroundColor Yellow +$argsString = $args -join ' ' +$command = "python build.py $argsString" + +Invoke-Expression $command + +if ($LASTEXITCODE -ne 0) { + Write-Host "[ERROR] Build failed" -ForegroundColor Red + Read-Host "Press Enter to exit" + exit 1 +} \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/01k/scripts/build.py new file mode 100644 index 00000000..1c99c2c8 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/scripts/build.py @@ -0,0 +1,357 @@ +#!/usr/bin/env python3 +""" +Fortran CFD 项目构建脚本 (Python版) +支持 Intel oneAPI 环境的自动化构建 +""" + +import os +import sys +import subprocess +import shutil +from pathlib import Path +import argparse +import platform +import time + +class Colors: + """终端颜色""" + if platform.system() == "Windows": + # Windows 启用 ANSI + os.system("") + + HEADER = '\033[95m' + BLUE = '\033[94m' + CYAN = '\033[96m' + GREEN = '\033[92m' + YELLOW = '\033[93m' + RED = '\033[91m' + MAGENTA = '\033[95m' + DARK_GRAY = '\033[90m' # 添加这个 + END = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + +def print_color(text, color=Colors.END): + """彩色打印""" + print(f"{color}{text}{Colors.END}") + +def print_header(text): + """打印标题""" + print_color("\n" + "="*60, Colors.CYAN) + print_color(f" {text}", Colors.BOLD + Colors.CYAN) + print_color("="*60 + "\n", Colors.CYAN) + +def print_step(step, total, message): + """打印步骤""" + print_color(f"[{step}/{total}] {message}...", Colors.YELLOW) + +def print_success(message): + """打印成功""" + print_color(f"✓ {message}", Colors.GREEN) + +def print_error(message): + """打印错误""" + print_color(f"✗ {message}", Colors.RED) + +def print_warning(message): + """打印警告""" + print_color(f"! {message}", Colors.YELLOW) + +def run_command(cmd, cwd=None, check=True, capture=True): + """运行命令""" + print_color(f" $ {' '.join(cmd)}", Colors.BLUE) + + try: + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=capture, + text=True, + encoding='utf-8', + errors='ignore', + shell=False + ) + + if capture and result.stdout: + print(result.stdout) + if capture and result.stderr: + print_color(result.stderr, Colors.YELLOW) + + if check and result.returncode != 0: + print_error(f"Command failed with exit code: {result.returncode}") + return False + + return True + + except FileNotFoundError as e: + print_error(f"Command not found: {cmd[0]}") + if check: + raise + return False + except Exception as e: + print_error(f"Command execution failed: {e}") + return False + +def setup_intel_environment(): + """设置 Intel oneAPI 环境""" + setvars_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + ] + + setvars_path = None + for path in setvars_paths: + if os.path.exists(path): + setvars_path = path + break + + if not setvars_path: + print_error("Intel oneAPI setvars.bat not found.") + print_warning("Please install Intel oneAPI or update the path in build.py") + return False + + # 创建临时的 batch 文件 + temp_bat = "temp_setvars.bat" + with open(temp_bat, 'w') as f: + f.write(f'@echo off\n') + f.write(f'call "{setvars_path}" > nul 2>&1\n') + f.write(f'set\n') + + try: + # 运行并捕获环境变量 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + shell=True, + encoding='utf-8', + errors='ignore' + ) + + # 解析并设置环境变量 + for line in result.stdout.split('\n'): + if '=' in line: + key, value = line.split('=', 1) + os.environ[key.strip()] = value.strip() + + os.remove(temp_bat) + print_success("Intel oneAPI environment configured") + return True + + except Exception as e: + print_error(f"Failed to setup Intel environment: {e}") + if os.path.exists(temp_bat): + os.remove(temp_bat) + return False + +def build_project(args): + """构建项目主函数""" + start_time = time.time() + + print_header(f"Fortran CFD Project Builder") + print_color(f"Build type: {args.build_type}", Colors.CYAN) + print_color(f"Run tests: {args.run_tests}", Colors.CYAN) + print() + + # 获取项目根目录(脚本所在目录的父目录) + script_dir = Path(__file__).parent + project_root = script_dir.parent + os.chdir(project_root) + + print_color(f"Project root: {project_root}", Colors.BLUE) + + # 步骤1: 设置 Intel 环境 + print_step(1, 4, "Setting up Intel Fortran compiler") + if not setup_intel_environment(): + return False + + # 步骤2: 准备构建目录 + print_step(2, 4, "Preparing build directory") + build_dir = project_root / "build" + + if args.clean and build_dir.exists(): + try: + shutil.rmtree(build_dir) + print_success("Cleaned build directory") + except Exception as e: + print_error(f"Failed to clean build directory: {e}") + if not args.force: + return False + + build_dir.mkdir(exist_ok=True) + os.chdir(build_dir) + + # 步骤3: 配置项目 + print_step(3, 4, "Configuring project") + + cmake_cmd = [ + "cmake", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-T", "fortran=ifx", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ".." + ] + + if not run_command(cmake_cmd, check=not args.force): + if not args.force: + return False + + # 步骤4: 构建项目 + print_step(4, 4, "Building project") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + f"-j{args.jobs}" if args.jobs > 1 else "" + ] + + print(f"build_cmd={build_cmd}") + build_cmd = [c for c in build_cmd if c] # 移除空字符串 + + if not run_command(build_cmd, check=not args.force): + if not args.force: + return False + + build_time = time.time() - start_time + print_success(f"Build completed in {build_time:.1f} seconds") + + # 运行测试 + if args.run_tests: + print_header("Running Tests") + run_tests(args.build_type) + + print_header("Build Summary") + print_color(f"Build directory: {build_dir}", Colors.GREEN) + print_color(f"Build type: {args.build_type}", Colors.GREEN) + print_color(f"Total time: {build_time:.1f}s", Colors.GREEN) + + return True + +def run_tests(build_type): + """运行测试""" + tests = [ + ("test_simple", "Simple functionality test"), + ("test_factory", "Factory pattern test"), + ] + + for test_name, description in tests: + # 修复:在这里直接访问当前循环的 test_name + _run_single_test(test_name, description, build_type) + +def _run_single_test(test_name, description, build_type): + """运行单个测试(修复作用域问题)""" + # 可能的测试可执行文件路径 + possible_paths = [ + Path(f"./{build_type}/{test_name}.exe"), + Path(f"./tests/{build_type}/{test_name}.exe"), + Path(f"./tests/{test_name}.exe"), + Path(f"{test_name}.exe"), + Path(f"./{test_name}.exe"), + ] + + test_exe = None + + # 尝试所有可能的路径 + for path in possible_paths: + if path.exists(): + test_exe = path + break + + if test_exe: + print_color(f"\n[TEST] {description}...", Colors.MAGENTA) + print_color("-" * 50, Colors.DARK_GRAY) + + try: + result = subprocess.run( + [str(test_exe)], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + + if result.stdout: + print(result.stdout) + + if result.returncode == 0: + print_success(f"{test_name} passed") + else: + print_error(f"{test_name} failed (exit code: {result.returncode})") + if result.stderr: + print_color(result.stderr, Colors.YELLOW) + + except Exception as e: + print_error(f"{test_name} failed: {e}") + else: + print_warning(f"{test_name}.exe not found. Searched paths:") + for path in possible_paths: + print_color(f" - {path}", Colors.YELLOW) + +def main(): + """主函数""" + parser = argparse.ArgumentParser(description="Fortran CFD Project Builder") + + parser.add_argument( + "--build-type", + choices=["Debug", "Release", "RelWithDebInfo", "MinSizeRel"], + default="Debug", + help="Build type (default: Debug)" + ) + + parser.add_argument( + "--clean", + action="store_true", + help="Clean build directory before building" + ) + + parser.add_argument( + "--no-tests", + action="store_true", + help="Skip running tests" + ) + + parser.add_argument( + "-j", "--jobs", + type=int, + default=0, + help="Number of parallel jobs (0 = use all cores)" + ) + + parser.add_argument( + "--force", + action="store_true", + help="Continue on errors" + ) + + parser.add_argument( + "--verbose", + action="store_true", + help="Verbose output" + ) + + args = parser.parse_args() + + if args.jobs == 0: + import multiprocessing + args.jobs = multiprocessing.cpu_count() + + args.run_tests = not args.no_tests + + try: + success = build_project(args) + if not success and not args.force: + sys.exit(1) + except KeyboardInterrupt: + print_color("\nBuild interrupted by user", Colors.YELLOW) + sys.exit(1) + except Exception as e: + print_error(f"Unexpected error: {e}") + if args.verbose: + import traceback + traceback.print_exc() + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/01k/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01k/src/CMakeLists.txt new file mode 100644 index 00000000..ee38952b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/src/CMakeLists.txt @@ -0,0 +1,14 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01k/src/core/CMakeLists.txt new file mode 100644 index 00000000..bcfd024d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/src/core/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/core/CMakeLists.txt +message(STATUS "配置核心模块...") + +add_library(core STATIC + registry.f90 + factory_interfaces.f90 +) + +target_include_directories(core PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 安装规则 +install(TARGETS core + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) +install(FILES + ${CMAKE_Fortran_MODULE_DIRECTORY}/registry_module.mod + ${CMAKE_Fortran_MODULE_DIRECTORY}/factory_interfaces.mod + DESTINATION include/fortran_cfd/core +) + +message(STATUS "核心模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/01k/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..a960167c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/01k/src/core/registry.f90 new file mode 100644 index 00000000..bf9028c3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/src/core/registry.f90 @@ -0,0 +1,318 @@ +! src/core/registry.f90 +module registry_module + use, intrinsic :: iso_fortran_env, only: real64 + use factory_interfaces, only: factory_procedure + implicit none + + private + + ! Public interface + public :: real64, component_info, component_registry + public :: register_component_simple, initialize_registry, cleanup_registry + public :: has_component, get_available_components + public :: registry_is_initialized, registry_get_size ! + + ! Type definitions (simplified, no factory for now) + type :: component_info + character(len=32) :: category = "" + character(len=32) :: name = "" + integer :: order = 0 + contains + procedure :: print => ci_print + end type component_info + + type :: component_registry_type + private + type(component_info), allocatable :: components(:) + integer :: count = 0 + integer :: capacity = 100 + logical :: verbose = .true. + logical :: initialized = .false. + contains + procedure :: register => cr_register + procedure :: get => cr_get + procedure :: list_all => cr_list_all + procedure :: clear => cr_clear + procedure :: size => cr_size + procedure :: is_initialized => cr_is_initialized ! + end type component_registry_type + + ! Global registry instance + type(component_registry_type), save :: component_registry + +contains + + ! ==================== PUBLIC API ==================== + + ! Initialize registry + subroutine initialize_registry(initial_capacity, verbose) + integer, optional, intent(in) :: initial_capacity + logical, optional, intent(in) :: verbose + + if (component_registry%initialized) then + if (component_registry%verbose) then + print *, "[INFO] Registry already initialized" + end if + return + end if + + if (present(initial_capacity)) then + component_registry%capacity = max(10, initial_capacity) + end if + + if (present(verbose)) then + component_registry%verbose = verbose + end if + + ! Allocate array + allocate(component_registry%components(component_registry%capacity)) + + component_registry%initialized = .true. + component_registry%count = 0 + + if (component_registry%verbose) then + print *, "[INIT] Registry initialized, capacity:", component_registry%capacity + end if + end subroutine initialize_registry + + ! Cleanup registry + subroutine cleanup_registry + call component_registry%clear() + if (component_registry%verbose) then + print *, "[CLEANUP] Registry cleaned up" + end if + end subroutine cleanup_registry + + ! Simple registration (no factory) + subroutine register_component_simple(category, name) + character(len=*), intent(in) :: category, name + + type(component_info) :: info + + info%category = to_lower(trim(adjustl(category))) + info%name = to_lower(trim(adjustl(name))) + info%order = 0 + + call component_registry%register(info) + end subroutine register_component_simple + + ! Check if component exists + function has_component(category, name) result(found) + character(len=*), intent(in) :: category, name + logical :: found + + type(component_info) :: info + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + info = component_registry%get(cat_lower, name_lower) + found = (len_trim(info%category) > 0) + end function has_component + + ! Get available components in a category + subroutine get_available_components(category, names, orders) + character(len=*), intent(in) :: category + character(len=:), allocatable, intent(out), optional :: names(:) + integer, allocatable, intent(out), optional :: orders(:) + + character(len=32) :: cat_lower + integer :: i, count, idx + type(component_info) :: info + + cat_lower = to_lower(trim(adjustl(category))) + + ! Count components in this category + count = 0 + do i = 1, component_registry%count + if (component_registry%components(i)%category == cat_lower) then + count = count + 1 + end if + end do + + ! Allocate arrays if requested + if (present(names)) then + allocate(character(len=32) :: names(count)) + end if + + if (present(orders)) then + allocate(orders(count)) + end if + + ! Fill arrays + idx = 1 + do i = 1, component_registry%count + if (component_registry%components(i)%category == cat_lower) then + info = component_registry%components(i) + if (present(names)) then + names(idx) = info%name + end if + if (present(orders)) then + orders(idx) = info%order + end if + idx = idx + 1 + end if + end do + end subroutine get_available_components + + ! Public function to check if registry is initialized + function registry_is_initialized() result(is_initialized) + logical :: is_initialized + is_initialized = component_registry%is_initialized() + end function registry_is_initialized + + ! Public function to get registry size + function registry_get_size() result(size_val) + integer :: size_val + size_val = component_registry%size() + end function registry_get_size + + ! ==================== COMPONENT INFO METHODS ==================== + + subroutine ci_print(this) + class(component_info), intent(in) :: this + + if (this%order > 0) then + print *, " [", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")]" + else + print *, " [", trim(this%category), ".", trim(this%name), "]" + end if + end subroutine ci_print + + ! ==================== REGISTRY INTERNAL METHODS ==================== + + subroutine cr_register(this, info) + class(component_registry_type), intent(inout) :: this + type(component_info), intent(in) :: info + + type(component_info), allocatable :: temp(:) + integer :: i + + if (.not. this%initialized) then + error stop "[ERROR] Registry not initialized, call initialize_registry first" + end if + + ! Check if already exists + do i = 1, this%count + if (this%components(i)%category == info%category .and. & + this%components(i)%name == info%name) then + if (this%verbose) then + print *, "[WARN] Overwriting: ", & + trim(info%category), ".", trim(info%name) + end if + this%components(i) = info + return + end if + end do + + ! Expand array if needed + if (this%count >= this%capacity) then + this%capacity = this%capacity * 2 + allocate(temp(this%capacity)) + temp(1:this%count) = this%components(1:this%count) + call move_alloc(temp, this%components) + + if (this%verbose) then + print *, "[INFO] Registry expanded to capacity:", this%capacity + end if + end if + + ! Add component + this%count = this%count + 1 + this%components(this%count) = info + + if (this%verbose) then + print *, "[OK] Registered: ", trim(info%category), ".", trim(info%name) + end if + end subroutine cr_register + + function cr_get(this, category, name) result(info) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + type(component_info) :: info + + integer :: i + + ! Initialize return value as empty + info%category = "" + info%name = "" + info%order = 0 + + if (.not. this%initialized) then + return + end if + + do i = 1, this%count + if (this%components(i)%category == category .and. & + this%components(i)%name == name) then + info = this%components(i) + return + end if + end do + end function cr_get + + subroutine cr_list_all(this) + class(component_registry_type), intent(in) :: this + integer :: i + + if (.not. this%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + print *, "=== Registry Contents (", this%count, " components) ===" + + if (this%count == 0) then + print *, " (empty)" + return + end if + + ! Show components grouped by category + do i = 1, this%count + call this%components(i)%print() + end do + + print *, "===========================================" + end subroutine cr_list_all + + subroutine cr_clear(this) + class(component_registry_type), intent(inout) :: this + + if (allocated(this%components)) then + deallocate(this%components) + end if + + this%count = 0 + this%capacity = 100 + this%initialized = .false. + end subroutine cr_clear + + integer function cr_size(this) + class(component_registry_type), intent(in) :: this + cr_size = this%count + end function cr_size + + logical function cr_is_initialized(this) + class(component_registry_type), intent(in) :: this + cr_is_initialized = this%initialized + end function cr_is_initialized + + ! ==================== UTILITY FUNCTIONS ==================== + + function to_lower(str) result(lower_str) + character(len=*), intent(in) :: str + character(len=len(str)) :: lower_str + integer :: i + + do i = 1, len(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + lower_str(i:i) = char(ichar(str(i:i)) + 32) + else + lower_str(i:i) = str(i:i) + end if + end do + end function to_lower + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01k/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..46964ce7 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/src/infrastructure/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "配置基础设施模块...") + +add_library(infrastructure STATIC + config.f90 + mesh.f90 +) + +target_link_libraries(infrastructure PRIVATE core) + +target_include_directories(infrastructure PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS infrastructure + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "基础设施模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/01k/src/infrastructure/config.f90 new file mode 100644 index 00000000..d3b1e0df --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/src/infrastructure/config.f90 @@ -0,0 +1,90 @@ +! src/infrastructure/config.f90 +module config_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, cfd_config, config_print, config_with_reconstruction + + ! CFD configuration type + type :: cfd_config + character(len=20) :: ic_type = "step" + character(len=20) :: recon_scheme = "eno" + character(len=20) :: flux_type = "rusanov" + integer :: rk_order = 1 + real(real64) :: wave_speed = 1.0_real64 + real(real64) :: final_time = 0.625_real64 + real(real64) :: dt = 0.025_real64 + character(len=20) :: boundary_type = "periodic" + real(real64) :: left_boundary_value = 1.0_real64 + real(real64) :: right_boundary_value = 2.0_real64 + integer :: spatial_order = 2 + logical :: verbose = .true. + end type cfd_config + +contains + + subroutine config_print(this) + type(cfd_config), intent(in) :: this + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(this%ic_type) + print *, "Reconstruction: ", trim(this%recon_scheme), " (order:", this%spatial_order, ")" + print *, "Flux type: ", trim(this%flux_type) + print *, "Time integration: RK", this%rk_order + print *, "Wave speed: ", this%wave_speed + print *, "Final time: ", this%final_time + print *, "Time step: ", this%dt + print *, "Boundary: ", trim(this%boundary_type) + if (trim(this%boundary_type) == 'dirichlet') then + print *, " Dirichlet values: [", this%left_boundary_value, ", ", & + this%right_boundary_value, "]" + end if + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(this, scheme, order) + type(cfd_config), intent(inout) :: this + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + character(len=20) :: scheme_lower + + ! Convert to lowercase + scheme_lower = scheme + call to_lower_inplace(scheme_lower) + this%recon_scheme = trim(adjustl(scheme_lower)) + + ! Set order + if (present(order)) then + this%spatial_order = order + else + ! Smart defaults + if (index(this%recon_scheme, 'weno') > 0) then + this%spatial_order = 5 + else if (trim(this%recon_scheme) == 'eno') then + this%spatial_order = 3 + else + print *, "[ERROR] Unsupported reconstruction scheme: ", trim(this%recon_scheme) + return + end if + end if + + if (this%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(this%recon_scheme), & + " Order: ", this%spatial_order + end if + end subroutine config_with_reconstruction + + subroutine to_lower_inplace(str) + character(len=*), intent(inout) :: str + integer :: i + + do i = 1, len_trim(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + str(i:i) = char(ichar(str(i:i)) + 32) + end if + end do + end subroutine to_lower_inplace + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/01k/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..896969bd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/src/infrastructure/mesh.f90 @@ -0,0 +1,74 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, mesh_type, mesh_init, mesh_print_info + + ! mesh + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer :: ncells = 40 + integer :: nnodes + integer :: nx + real(wp), allocatable :: x(:) ! + real(wp), allocatable :: xcc(:) ! + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer, optional, intent(in) :: ncells + + integer :: i + + ! Set + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! computation + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! node + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! cell + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== ===" + print *, ": [", this%xmin, ", ", this%xmax, "]" + print *, ": ", this%ncells + print *, ": ", this%nnodes + print *, " dx: ", this%dx + print *, " L: ", this%L + print *, "==========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01k/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01k/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..3fde7746 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,28 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux STATIC + base.f90 + rusanov.f90 +) + +#target_link_libraries(flux PRIVATE core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 明确设置不使用 /Qm64 选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + set_target_properties(flux PROPERTIES + Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /Qm64" # 明确设置,避免继承问题 + ) +endif() + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/01k/src/numerics/flux/base.f90 new file mode 100644 index 00000000..7080a7ae --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/src/numerics/flux/base.f90 @@ -0,0 +1,30 @@ +!src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + procedure :: print_basic_info => flux_print_basic ! 添加辅助方法 + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + + subroutine flux_print_basic(this) + class(flux_calculator_base), intent(in) :: this + print *, " Name: ", trim(this%name) + end subroutine flux_print_basic + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/01k/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..7140f710 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/src/numerics/flux/rusanov.f90 @@ -0,0 +1,41 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + + ! 构造函数接口 + interface rusanov_flux + module procedure create_rusanov_flux + end interface + +contains + + ! 构造函数 + type(rusanov_flux) function create_rusanov_flux() result(this) + this%name = "Rusanov" + this%wave_speed_default = 1.0_real64 + end function create_rusanov_flux + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Flux calculator information:" + call this%print_basic_info() + + ! 添加Rusanov特有信息 + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01k/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..5e4b938d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,22 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor STATIC + base.f90 + eno.f90 + weno3.f90 +) + +target_link_libraries(reconstructor PRIVATE core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/01k/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..53798d02 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/src/numerics/reconstructor/base.f90 @@ -0,0 +1,33 @@ +!src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + procedure :: print_basic_info => reconstructor_print_basic ! 添加一个辅助方法 + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + + subroutine reconstructor_print_basic(this) + class(reconstructor_base), intent(in) :: this + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_print_basic + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/01k/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..a468b82e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + + ! 构造函数接口 + interface eno_reconstructor + module procedure create_eno_reconstructor + end interface + +contains + + ! 构造函数 + type(eno_reconstructor) function create_eno_reconstructor() result(this) + this%name = "ENO" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_eno_reconstructor + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加ENO特有信息 + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/01k/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..5e954291 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno3_reconstructor + + type, extends(reconstructor_base) :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + + ! 构造函数接口 + interface weno3_reconstructor + module procedure create_weno3_reconstructor + end interface + +contains + + ! 构造函数 + type(weno3_reconstructor) function create_weno3_reconstructor() result(this) + this%name = "WENO3" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_weno3_reconstructor + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO3特有信息 + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01k/tests/CMakeLists.txt new file mode 100644 index 00000000..0cd64b0c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/tests/CMakeLists.txt @@ -0,0 +1,43 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +add_executable(test_minimal_simple test_minimal_simple.f90) + +message(STATUS "CMAKE_Fortran_MODULE_DIRECTORY=${CMAKE_Fortran_MODULE_DIRECTORY}") + +#target_include_directories( test_minimal_simple +# PRIVATE +# ${CMAKE_Fortran_MODULE_DIRECTORY} +#) + +target_link_libraries( test_minimal_simple + PRIVATE + infrastructure +) + +add_executable(test_simple_link test_simple_link.f90) +target_link_libraries(test_simple_link + PRIVATE + reconstructor + flux +) + + +#add_executable(test_factory_simple test_factory_simple.f90) + +#target_link_libraries( test_factory_simple +# PRIVATE +# core +# infrastructure +# reconstructor +# flux +#) + +add_executable(test_factory_simple test_factory_simple.f90) +target_link_libraries(test_factory_simple + PRIVATE + core + infrastructure + reconstructor + flux +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/tests/test_factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/01k/tests/test_factory.f90 new file mode 100644 index 00000000..c62d5f8a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/tests/test_factory.f90 @@ -0,0 +1,154 @@ +! tests/test_factory.f90 +program test_factory + use registry_module + use config_module + use mesh_module + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + class(*), allocatable :: instance + class(reconstructor_base), allocatable :: recon + class(flux_calculator_base), allocatable :: flux_calc + real(wp), allocatable :: q(:), qL(:), qR(:), flux(:) + integer :: i, n + + print *, "=== Factory Pattern Test ===" + print *, "" + + ! Initialize systems + call initialize_registry(verbose=.true.) + + ! Register components with factories + print *, "1. Registering components with factories..." + call register_component_with_factory("reconstructor", "eno", create_eno, 3) + call register_component_with_factory("reconstructor", "weno3", create_weno3, 3) + call register_component_with_factory("flux", "rusanov", create_rusanov) + + call component_registry%list_all() + print *, "" + + ! Test creating ENO reconstructor + print *, "2. Creating ENO reconstructor..." + call create_component("reconstructor", "eno", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (eno_reconstructor) + allocate(recon, source=inst) + print *, "ENO reconstructor created successfully" + call recon%info() + print *, "" + + ! Test reconstruction + n = 10 + allocate(q(0:n+1), qL(n), qR(n)) + + ! Initialize test data (sine wave) + do i = 0, n+1 + q(i) = sin(2.0_wp * 3.141592653589793_wp * real(i-1, wp) / real(n, wp)) + end do + + print *, "Testing ENO reconstruction..." + call recon%reconstruct(q, qL, qR) + + print *, "q (internal):" + do i = 1, n + write(*, '(I3, F10.6)') i, q(i) + end do + + print *, "qL (left interface values):" + do i = 1, n + write(*, '(I3, F10.6)') i, qL(i) + end do + + deallocate(q, qL, qR) + class default + print *, "[ERROR] Wrong type for ENO reconstructor" + end select + else + print *, "[ERROR] Failed to create ENO reconstructor" + end if + print *, "" + + ! Test creating WENO3 reconstructor + print *, "3. Creating WENO3 reconstructor..." + if (allocated(instance)) deallocate(instance) + call create_component("reconstructor", "weno3", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (weno3_reconstructor) + if (allocated(recon)) deallocate(recon) + allocate(recon, source=inst) + print *, "WENO3 reconstructor created successfully" + call recon%info() + class default + print *, "[ERROR] Wrong type for WENO3 reconstructor" + end select + else + print *, "[ERROR] Failed to create WENO3 reconstructor" + end if + print *, "" + + ! Test creating Rusanov flux calculator + print *, "4. Creating Rusanov flux calculator..." + if (allocated(instance)) deallocate(instance) + call create_component("flux", "rusanov", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (rusanov_flux) + allocate(flux_calc, source=inst) + print *, "Rusanov flux calculator created successfully" + call flux_calc%info() + print *, "" + + ! Test flux computation + n = 5 + allocate(qL(n), qR(n), flux(n)) + + ! Initialize test data + do i = 1, n + qL(i) = 1.0_wp + 0.1_wp * real(i-1, wp) + qR(i) = 1.0_wp + 0.1_wp * real(i, wp) + end do + + print *, "Testing Rusanov flux computation..." + call flux_calc%compute(qL, qR, flux, 1.0_wp) + + print *, "qL:" + do i = 1, n + write(*, '(I3, F10.6)') i, qL(i) + end do + + print *, "qR:" + do i = 1, n + write(*, '(I3, F10.6)') i, qR(i) + end do + + print *, "Flux:" + do i = 1, n + write(*, '(I3, F10.6)') i, flux(i) + end do + + deallocate(qL, qR, flux) + class default + print *, "[ERROR] Wrong type for Rusanov flux" + end select + else + print *, "[ERROR] Failed to create Rusanov flux calculator" + end if + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Factory pattern test completed successfully ===" + +end program test_factory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/tests/test_factory_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/01k/tests/test_factory_simple.f90 new file mode 100644 index 00000000..d4139bb1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/tests/test_factory_simple.f90 @@ -0,0 +1,86 @@ +!tests/test_factory_simple.f90 +program test_factory_simple + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module, only: initialize_registry, cleanup_registry, & + register_component_simple, has_component + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Basic systems + print *, "1. Testing basic systems..." + print *, "-----------------------------" + call config_print(config) + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Creating reconstructors + print *, "2. Testing reconstructors..." + print *, "------------------------------" + + ! 创建并测试ENO重构器 + print *, "Creating ENO reconstructor..." + eno = eno_reconstructor() ! 使用构造函数 + call eno%info() ! 必须调用info方法 + + print *, "" + print *, "Creating WENO3 reconstructor..." + weno3 = weno3_reconstructor() ! 使用构造函数 + call weno3%info() ! 必须调用info方法 + print *, "" + + ! Test 3: Creating flux calculator + print *, "3. Testing flux calculator..." + print *, "-------------------------------" + + print *, "Creating Rusanov flux calculator..." + rusanov = rusanov_flux() ! 使用构造函数 + call rusanov%info() ! 必须调用info方法 + print *, "" + + ! Test 4: Registry integration + print *, "4. Testing registry..." + print *, "----------------------" + + call initialize_registry(verbose=.true.) + + ! Register components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! Check registration + if (has_component("reconstructor", "eno")) then + print *, "[OK] ENO reconstructor registered successfully" + end if + + if (has_component("reconstructor", "weno3")) then + print *, "[OK] WENO3 reconstructor registered successfully" + end if + + if (has_component("flux", "rusanov")) then + print *, "[OK] Rusanov flux registered successfully" + end if + + print *, "" + call cleanup_registry() + + print *, "=== Factory pattern simple test completed successfully ===" + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/tests/test_minimal.f90 b/example/1d-linear-convection/weno3/fortran/registry/01k/tests/test_minimal.f90 new file mode 100644 index 00000000..807d0bc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/tests/test_minimal.f90 @@ -0,0 +1,108 @@ +! tests/test_minimal.f90 +program test_minimal + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i ! Declare loop variable here + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", component_registry%size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components + print *, "5. Testing available components" + print *, "--------------------------------" + + ! Use a separate block to avoid allocation issues + call test_available_components() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Minimal test completed successfully ===" + +contains + + ! Internal subroutine for testing available components + subroutine test_available_components() + character(len=:), allocatable :: names(:) + integer, allocatable :: orders(:) + integer :: j + + call get_available_components("reconstructor", names, orders) + + if (allocated(names)) then + print *, "Reconstructors:" + do j = 1, size(names) + print *, " - ", trim(names(j)) + if (allocated(orders)) then + print *, " Order: ", orders(j) + end if + end do + else + print *, "No reconstructors found" + end if + end subroutine test_available_components + +end program test_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/tests/test_minimal_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/01k/tests/test_minimal_simple.f90 new file mode 100644 index 00000000..6213e8f8 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/tests/test_minimal_simple.f90 @@ -0,0 +1,84 @@ +! tests/test_minimal_simple.f90 +program test_minimal_simple + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Simple Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components (simple version) + print *, "5. Testing registry functionality" + print *, "---------------------------------" + print *, "Registry initialized: ", registry_is_initialized() + print *, "Number of components: ", registry_get_size() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Simple test completed successfully ===" + +end program test_minimal_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01k/tests/test_simple_link.f90 b/example/1d-linear-convection/weno3/fortran/registry/01k/tests/test_simple_link.f90 new file mode 100644 index 00000000..807d0bc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01k/tests/test_simple_link.f90 @@ -0,0 +1,108 @@ +! tests/test_minimal.f90 +program test_minimal + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i ! Declare loop variable here + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", component_registry%size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components + print *, "5. Testing available components" + print *, "--------------------------------" + + ! Use a separate block to avoid allocation issues + call test_available_components() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Minimal test completed successfully ===" + +contains + + ! Internal subroutine for testing available components + subroutine test_available_components() + character(len=:), allocatable :: names(:) + integer, allocatable :: orders(:) + integer :: j + + call get_available_components("reconstructor", names, orders) + + if (allocated(names)) then + print *, "Reconstructors:" + do j = 1, size(names) + print *, " - ", trim(names(j)) + if (allocated(orders)) then + print *, " Order: ", orders(j) + end if + end do + else + print *, "No reconstructors found" + end if + end subroutine test_available_components + +end program test_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01l/CMakeLists.txt new file mode 100644 index 00000000..ef66d584 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/README.md b/example/1d-linear-convection/weno3/fortran/registry/01l/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/01l/scripts/build.bat new file mode 100644 index 00000000..6fd6dc03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/scripts/build.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Project Builder +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python build script with full Intel environment support... +echo. + +python build.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Build failed + pause + exit /b 1 +) + +echo. +echo [INFO] Build completed successfully! +echo. +echo [INFO] To run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/01l/scripts/build.py new file mode 100644 index 00000000..3bf6d537 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/scripts/build.py @@ -0,0 +1,629 @@ +#!/usr/bin/env python3 +""" +Fortran CFD Project Builder - 完整Python解决方案 +在Python内部处理Intel oneAPI环境配置 +""" + +import os +import sys +import subprocess +import shutil +import argparse +import time +import platform +import tempfile +from pathlib import Path + +class IntelEnvironment: + """Intel oneAPI环境管理器""" + + def __init__(self): + self.setvars_path = None + self.env_vars = {} + + def find_setvars(self): + """查找setvars.bat文件""" + possible_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + r"C:\Program Files (x86)\Intel\oneAPI\compiler\latest\env\vars.bat", + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\setvars.bat"), + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\compiler\latest\env\vars.bat"), + ] + + for path in possible_paths: + if os.path.exists(path): + self.setvars_path = path + return True + + return False + + def setup_environment(self): + """设置Intel环境""" + if not self.find_setvars(): + return False + + try: + # 创建临时的批处理文件来捕获环境变量 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + f.write(f'@echo off\n') + f.write(f'call "{self.setvars_path}" >nul 2>&1\n') + f.write(f'set\n') # 输出所有环境变量 + temp_bat = f.name + + # 运行批处理文件并捕获输出 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + shell=True + ) + + # 解析环境变量 + for line in result.stdout.split('\n'): + line = line.strip() + if '=' in line: + key, value = line.split('=', 1) + self.env_vars[key.strip()] = value.strip() + + # 清理临时文件 + os.unlink(temp_bat) + + # 更新当前进程的环境变量 + os.environ.update(self.env_vars) + + return True + + except Exception as e: + print(f"设置Intel环境失败: {e}") + return False + + def get_compiler_info(self): + """获取编译器信息""" + info = {} + + # 检查ifx编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + env={**os.environ, **self.env_vars} if self.env_vars else os.environ + ) + + if result.returncode == 0: + for line in result.stdout.split('\n'): + if 'Version' in line or '版本' in line: + info['ifx_version'] = line.strip() + break + except: + pass + + # 检查环境变量 + info['ifx_root'] = self.env_vars.get('IFX_ROOT', '') + info['compiler_root'] = self.env_vars.get('ONEAPI_ROOT', '') + + return info + +class BuildSystem: + """构建系统主类""" + + def __init__(self): + self.project_root = Path(__file__).parent.parent + self.build_dir = self.project_root / "build" + self.intel_env = IntelEnvironment() + + # 设置控制台编码 + if sys.platform == "win32": + try: + import ctypes + # 设置控制台输出为UTF-8 + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + except: + pass + + def print_header(self, text): + """打印标题""" + print(f"\n{'='*70}") + print(f" {text}") + print(f"{'='*70}\n") + + def print_step(self, step, total, message): + """打印步骤""" + print(f"[{step}/{total}] {message}...") + + def print_success(self, message): + """打印成功""" + print(f"\033[92m✓ {message}\033[0m") + + def print_error(self, message): + """打印错误""" + print(f"\033[91m✗ {message}\033[0m") + + def print_warning(self, message): + """打印警告""" + print(f"\033[93m! {message}\033[0m") + + def print_info(self, message): + """打印信息""" + print(f"\033[94mℹ {message}\033[0m") + + def check_prerequisites(self): + """检查前提条件""" + self.print_step(1, 6, "检查前提条件") + + # 检查Python版本 + python_version = sys.version.split()[0] + self.print_info(f"Python版本: {python_version}") + + # 检查平台 + self.print_info(f"平台: {platform.system()} {platform.release()}") + self.print_info(f"处理器核心数: {os.cpu_count()}") + + # 检查CMake + try: + result = subprocess.run( + ["cmake", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + version_line = result.stdout.split('\n')[0] + self.print_success(f"CMake: {version_line}") + else: + self.print_error("CMake未找到") + return False + except FileNotFoundError: + self.print_error("CMake未安装") + return False + + return True + + def setup_intel_environment(self, args): + """设置Intel环境""" + self.print_step(2, 6, "配置Intel oneAPI环境") + + if not self.intel_env.find_setvars(): + self.print_warning("未找到Intel oneAPI setvars.bat") + self.print_info("将尝试使用系统环境中的编译器") + + # 检查是否能直接访问编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + self.print_success("Intel编译器在系统PATH中找到") + return True + else: + self.print_warning("Intel编译器未在PATH中找到") + except: + self.print_warning("无法访问Intel编译器") + + return True # 继续,让CMake自己找编译器 + + # 设置环境 + if self.intel_env.setup_environment(): + compiler_info = self.intel_env.get_compiler_info() + + if compiler_info.get('ifx_version'): + self.print_success(f"Intel Fortran编译器: {compiler_info['ifx_version']}") + elif compiler_info.get('ifx_root'): + self.print_success(f"Intel编译器路径: {compiler_info['ifx_root']}") + else: + self.print_success("Intel oneAPI环境配置完成") + + return True + else: + self.print_warning("Intel环境配置失败,将继续使用系统环境") + return True + + def clean_build_directory(self, args): + """清理构建目录""" + if args.clean and self.build_dir.exists(): + self.print_info("清理构建目录...") + try: + shutil.rmtree(self.build_dir) + self.print_success("构建目录已清理") + except Exception as e: + self.print_error(f"清理失败: {e}") + if not args.force: + return False + return True + + def run_command(self, cmd, cwd=None, check=True, env=None): + """运行命令""" + if isinstance(cmd, list): + cmd_str = ' '.join(str(c) for c in cmd if c) + else: + cmd_str = str(cmd) + + print(f" \033[96m$\033[0m {cmd_str}") + + try: + # 合并环境变量 + exec_env = os.environ.copy() + if env: + exec_env.update(env) + if self.intel_env.env_vars: + exec_env.update(self.intel_env.env_vars) + + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=False, + env=exec_env + ) + + # 处理输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or '完成' in line or '生成' in line: + print(f" \033[92m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + if check and result.returncode != 0: + self.print_error(f"命令执行失败,退出码: {result.returncode}") + return False + + return True + + except Exception as e: + self.print_error(f"命令执行异常: {e}") + return False + + def configure_cmake(self, args): + """配置CMake""" + self.print_step(3, 6, "配置CMake项目") + + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ] + + if args.compiler == "ifx": + cmake_cmd.extend(["-T", "fortran=ifx"]) + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + success = self.run_command(cmake_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("CMake配置完成") + else: + self.print_error("CMake配置失败") + + return success + + def build_project(self, args): + """构建项目""" + self.print_step(4, 6, "构建项目") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + success = self.run_command(build_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("项目构建完成") + else: + self.print_error("构建失败") + + return success + + def run_tests_with_environment(self, test_exe): + """运行单个测试,确保有Intel环境""" + try: + # 创建临时的批处理文件来运行测试 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'"{test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'"{test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + return result + + except Exception as e: + print(f"运行测试失败: {e}") + return None + + def run_tests(self, args): + """运行测试""" + self.print_step(5, 6, "运行测试") + + # 查找测试可执行文件 + test_dir = self.build_dir / "bin" / args.build_type + if not test_dir.exists(): + test_dir = self.build_dir / "bin" + if not test_dir.exists(): + test_dir = self.build_dir + + test_files = list(test_dir.glob("test_*.exe")) + + if not test_files: + self.print_warning("未找到测试程序") + return True + + all_passed = True + + for test_exe in sorted(test_files): + test_name = test_exe.stem + self.print_info(f"运行测试: {test_name}") + print(f" {'-'*50}") + + # 运行测试 + result = self.run_tests_with_environment(str(test_exe)) + + if result is None: + self.print_error(f" {test_name} 运行失败") + all_passed = False + continue + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + print(f" {line}") + + if result.returncode == 0: + self.print_success(f" {test_name} 通过") + else: + self.print_error(f" {test_name} 失败 (退出码: {result.returncode})") + all_passed = False + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + print() # 空行 + + return all_passed + + def create_test_runner(self, args): + """创建独立的测试运行器""" + self.print_step(6, 6, "创建测试运行器") + + runner_path = self.build_dir / "run_tests.bat" + + content = f'''@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Test Runner +echo ======================================== +echo. + +REM Setup Intel oneAPI environment +set "SETVARS_PATH={self.intel_env.setvars_path or ''}" +if exist "%SETVARS_PATH%" ( + call "%SETVARS_PATH%" >nul + echo [INFO] Intel environment configured +) else ( + echo [WARNING] Intel environment not found + echo [WARNING] Tests may fail without runtime libraries +) + +echo. + +REM Run all test executables +set "TEST_COUNT=0" +set "PASS_COUNT=0" + +for %%f in ("bin\\{args.build_type}\\test_*.exe") do ( + set /a TEST_COUNT+=1 + echo [TEST %%f] + echo {'-'*50} + + %%f + if errorlevel 1 ( + echo [FAILED] %%f + ) else ( + echo [PASSED] %%f + set /a PASS_COUNT+=1 + ) + echo. +) + +echo ======================================== +echo Tests: %PASS_COUNT%/%TEST_COUNT% passed +if %PASS_COUNT% equ %TEST_COUNT% ( + echo [SUCCESS] All tests passed! +) else ( + echo [FAILURE] Some tests failed +) +echo ======================================== + +pause +''' + + with open(runner_path, 'w', encoding='utf-8') as f: + f.write(content) + + self.print_success(f"测试运行器已创建: {runner_path}") + self.print_info(f"使用方法: cd build && run_tests.bat") + + return runner_path + + def generate_report(self, args, build_time, tests_passed): + """生成构建报告""" + self.print_header("构建完成") + + print(f"项目: {self.project_root.name}") + print(f"构建类型: {args.build_type}") + print(f"编译器: {args.compiler}") + print(f"并行作业: {args.jobs}") + print(f"总耗时: {build_time:.1f}秒") + print(f"测试结果: {'全部通过' if tests_passed else '有失败'}") + + # 显示生成的可执行文件 + bin_dir = self.build_dir / "bin" / args.build_type + if bin_dir.exists(): + print(f"\n生成的可执行文件:") + for exe in sorted(bin_dir.glob("*.exe")): + size_mb = exe.stat().st_size / (1024 * 1024) + print(f" • {exe.name} ({size_mb:.2f} MB)") + + # 显示测试运行器信息 + runner_path = self.build_dir / "run_tests.bat" + if runner_path.exists(): + print(f"\n独立测试运行器:") + print(f" • {runner_path.name}") + print(f" 在Intel oneAPI环境中运行所有测试") + + def run(self): + """运行构建系统""" + parser = argparse.ArgumentParser( + description="Fortran CFD项目构建工具", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认构建 + %(prog)s --clean # 清理后构建 + %(prog)s --build-type Release # Release构建 + %(prog)s --no-tests # 只构建,不运行测试 + %(prog)s -j8 --verbose # 8线程并行构建,详细输出 + """ + ) + + parser.add_argument("--build-type", choices=["Debug", "Release"], + default="Debug", help="构建类型") + parser.add_argument("--compiler", choices=["ifx", "ifort"], + default="ifx", help="Fortran编译器") + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-tests", action="store_true", + help="跳过测试") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + + args = parser.parse_args() + + # 开始构建 + start_time = time.time() + + self.print_header("Fortran CFD 项目构建系统") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 检查前提条件 + if not self.check_prerequisites(): + if not args.force: + return 1 + + # 2. 设置Intel环境 + if not self.setup_intel_environment(args): + if not args.force: + return 1 + + # 3. 清理目录 + if not self.clean_build_directory(args): + if not args.force: + return 1 + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 4. 配置CMake + if not self.configure_cmake(args): + if not args.force: + return 1 + + # 5. 构建项目 + if not self.build_project(args): + if not args.force: + return 1 + + # 6. 运行测试和创建测试运行器 + tests_passed = True + if not args.no_tests: + tests_passed = self.run_tests(args) + + # 创建测试运行器 + self.create_test_runner(args) # 传递 args 参数 + + # 7. 生成报告 + build_time = time.time() - start_time + self.generate_report(args, build_time, tests_passed) + + return 0 if tests_passed else 1 + + except KeyboardInterrupt: + self.print_error("\n构建被用户中断") + return 1 + except Exception as e: + self.print_error(f"构建过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + builder = BuildSystem() + return builder.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/01l/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01l/src/CMakeLists.txt new file mode 100644 index 00000000..ee38952b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/src/CMakeLists.txt @@ -0,0 +1,14 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01l/src/core/CMakeLists.txt new file mode 100644 index 00000000..bcfd024d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/src/core/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/core/CMakeLists.txt +message(STATUS "配置核心模块...") + +add_library(core STATIC + registry.f90 + factory_interfaces.f90 +) + +target_include_directories(core PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 安装规则 +install(TARGETS core + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) +install(FILES + ${CMAKE_Fortran_MODULE_DIRECTORY}/registry_module.mod + ${CMAKE_Fortran_MODULE_DIRECTORY}/factory_interfaces.mod + DESTINATION include/fortran_cfd/core +) + +message(STATUS "核心模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/01l/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..a960167c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/01l/src/core/registry.f90 new file mode 100644 index 00000000..bf9028c3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/src/core/registry.f90 @@ -0,0 +1,318 @@ +! src/core/registry.f90 +module registry_module + use, intrinsic :: iso_fortran_env, only: real64 + use factory_interfaces, only: factory_procedure + implicit none + + private + + ! Public interface + public :: real64, component_info, component_registry + public :: register_component_simple, initialize_registry, cleanup_registry + public :: has_component, get_available_components + public :: registry_is_initialized, registry_get_size ! + + ! Type definitions (simplified, no factory for now) + type :: component_info + character(len=32) :: category = "" + character(len=32) :: name = "" + integer :: order = 0 + contains + procedure :: print => ci_print + end type component_info + + type :: component_registry_type + private + type(component_info), allocatable :: components(:) + integer :: count = 0 + integer :: capacity = 100 + logical :: verbose = .true. + logical :: initialized = .false. + contains + procedure :: register => cr_register + procedure :: get => cr_get + procedure :: list_all => cr_list_all + procedure :: clear => cr_clear + procedure :: size => cr_size + procedure :: is_initialized => cr_is_initialized ! + end type component_registry_type + + ! Global registry instance + type(component_registry_type), save :: component_registry + +contains + + ! ==================== PUBLIC API ==================== + + ! Initialize registry + subroutine initialize_registry(initial_capacity, verbose) + integer, optional, intent(in) :: initial_capacity + logical, optional, intent(in) :: verbose + + if (component_registry%initialized) then + if (component_registry%verbose) then + print *, "[INFO] Registry already initialized" + end if + return + end if + + if (present(initial_capacity)) then + component_registry%capacity = max(10, initial_capacity) + end if + + if (present(verbose)) then + component_registry%verbose = verbose + end if + + ! Allocate array + allocate(component_registry%components(component_registry%capacity)) + + component_registry%initialized = .true. + component_registry%count = 0 + + if (component_registry%verbose) then + print *, "[INIT] Registry initialized, capacity:", component_registry%capacity + end if + end subroutine initialize_registry + + ! Cleanup registry + subroutine cleanup_registry + call component_registry%clear() + if (component_registry%verbose) then + print *, "[CLEANUP] Registry cleaned up" + end if + end subroutine cleanup_registry + + ! Simple registration (no factory) + subroutine register_component_simple(category, name) + character(len=*), intent(in) :: category, name + + type(component_info) :: info + + info%category = to_lower(trim(adjustl(category))) + info%name = to_lower(trim(adjustl(name))) + info%order = 0 + + call component_registry%register(info) + end subroutine register_component_simple + + ! Check if component exists + function has_component(category, name) result(found) + character(len=*), intent(in) :: category, name + logical :: found + + type(component_info) :: info + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + info = component_registry%get(cat_lower, name_lower) + found = (len_trim(info%category) > 0) + end function has_component + + ! Get available components in a category + subroutine get_available_components(category, names, orders) + character(len=*), intent(in) :: category + character(len=:), allocatable, intent(out), optional :: names(:) + integer, allocatable, intent(out), optional :: orders(:) + + character(len=32) :: cat_lower + integer :: i, count, idx + type(component_info) :: info + + cat_lower = to_lower(trim(adjustl(category))) + + ! Count components in this category + count = 0 + do i = 1, component_registry%count + if (component_registry%components(i)%category == cat_lower) then + count = count + 1 + end if + end do + + ! Allocate arrays if requested + if (present(names)) then + allocate(character(len=32) :: names(count)) + end if + + if (present(orders)) then + allocate(orders(count)) + end if + + ! Fill arrays + idx = 1 + do i = 1, component_registry%count + if (component_registry%components(i)%category == cat_lower) then + info = component_registry%components(i) + if (present(names)) then + names(idx) = info%name + end if + if (present(orders)) then + orders(idx) = info%order + end if + idx = idx + 1 + end if + end do + end subroutine get_available_components + + ! Public function to check if registry is initialized + function registry_is_initialized() result(is_initialized) + logical :: is_initialized + is_initialized = component_registry%is_initialized() + end function registry_is_initialized + + ! Public function to get registry size + function registry_get_size() result(size_val) + integer :: size_val + size_val = component_registry%size() + end function registry_get_size + + ! ==================== COMPONENT INFO METHODS ==================== + + subroutine ci_print(this) + class(component_info), intent(in) :: this + + if (this%order > 0) then + print *, " [", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")]" + else + print *, " [", trim(this%category), ".", trim(this%name), "]" + end if + end subroutine ci_print + + ! ==================== REGISTRY INTERNAL METHODS ==================== + + subroutine cr_register(this, info) + class(component_registry_type), intent(inout) :: this + type(component_info), intent(in) :: info + + type(component_info), allocatable :: temp(:) + integer :: i + + if (.not. this%initialized) then + error stop "[ERROR] Registry not initialized, call initialize_registry first" + end if + + ! Check if already exists + do i = 1, this%count + if (this%components(i)%category == info%category .and. & + this%components(i)%name == info%name) then + if (this%verbose) then + print *, "[WARN] Overwriting: ", & + trim(info%category), ".", trim(info%name) + end if + this%components(i) = info + return + end if + end do + + ! Expand array if needed + if (this%count >= this%capacity) then + this%capacity = this%capacity * 2 + allocate(temp(this%capacity)) + temp(1:this%count) = this%components(1:this%count) + call move_alloc(temp, this%components) + + if (this%verbose) then + print *, "[INFO] Registry expanded to capacity:", this%capacity + end if + end if + + ! Add component + this%count = this%count + 1 + this%components(this%count) = info + + if (this%verbose) then + print *, "[OK] Registered: ", trim(info%category), ".", trim(info%name) + end if + end subroutine cr_register + + function cr_get(this, category, name) result(info) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + type(component_info) :: info + + integer :: i + + ! Initialize return value as empty + info%category = "" + info%name = "" + info%order = 0 + + if (.not. this%initialized) then + return + end if + + do i = 1, this%count + if (this%components(i)%category == category .and. & + this%components(i)%name == name) then + info = this%components(i) + return + end if + end do + end function cr_get + + subroutine cr_list_all(this) + class(component_registry_type), intent(in) :: this + integer :: i + + if (.not. this%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + print *, "=== Registry Contents (", this%count, " components) ===" + + if (this%count == 0) then + print *, " (empty)" + return + end if + + ! Show components grouped by category + do i = 1, this%count + call this%components(i)%print() + end do + + print *, "===========================================" + end subroutine cr_list_all + + subroutine cr_clear(this) + class(component_registry_type), intent(inout) :: this + + if (allocated(this%components)) then + deallocate(this%components) + end if + + this%count = 0 + this%capacity = 100 + this%initialized = .false. + end subroutine cr_clear + + integer function cr_size(this) + class(component_registry_type), intent(in) :: this + cr_size = this%count + end function cr_size + + logical function cr_is_initialized(this) + class(component_registry_type), intent(in) :: this + cr_is_initialized = this%initialized + end function cr_is_initialized + + ! ==================== UTILITY FUNCTIONS ==================== + + function to_lower(str) result(lower_str) + character(len=*), intent(in) :: str + character(len=len(str)) :: lower_str + integer :: i + + do i = 1, len(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + lower_str(i:i) = char(ichar(str(i:i)) + 32) + else + lower_str(i:i) = str(i:i) + end if + end do + end function to_lower + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01l/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..46964ce7 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/src/infrastructure/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "配置基础设施模块...") + +add_library(infrastructure STATIC + config.f90 + mesh.f90 +) + +target_link_libraries(infrastructure PRIVATE core) + +target_include_directories(infrastructure PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS infrastructure + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "基础设施模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/01l/src/infrastructure/config.f90 new file mode 100644 index 00000000..d3b1e0df --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/src/infrastructure/config.f90 @@ -0,0 +1,90 @@ +! src/infrastructure/config.f90 +module config_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, cfd_config, config_print, config_with_reconstruction + + ! CFD configuration type + type :: cfd_config + character(len=20) :: ic_type = "step" + character(len=20) :: recon_scheme = "eno" + character(len=20) :: flux_type = "rusanov" + integer :: rk_order = 1 + real(real64) :: wave_speed = 1.0_real64 + real(real64) :: final_time = 0.625_real64 + real(real64) :: dt = 0.025_real64 + character(len=20) :: boundary_type = "periodic" + real(real64) :: left_boundary_value = 1.0_real64 + real(real64) :: right_boundary_value = 2.0_real64 + integer :: spatial_order = 2 + logical :: verbose = .true. + end type cfd_config + +contains + + subroutine config_print(this) + type(cfd_config), intent(in) :: this + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(this%ic_type) + print *, "Reconstruction: ", trim(this%recon_scheme), " (order:", this%spatial_order, ")" + print *, "Flux type: ", trim(this%flux_type) + print *, "Time integration: RK", this%rk_order + print *, "Wave speed: ", this%wave_speed + print *, "Final time: ", this%final_time + print *, "Time step: ", this%dt + print *, "Boundary: ", trim(this%boundary_type) + if (trim(this%boundary_type) == 'dirichlet') then + print *, " Dirichlet values: [", this%left_boundary_value, ", ", & + this%right_boundary_value, "]" + end if + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(this, scheme, order) + type(cfd_config), intent(inout) :: this + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + character(len=20) :: scheme_lower + + ! Convert to lowercase + scheme_lower = scheme + call to_lower_inplace(scheme_lower) + this%recon_scheme = trim(adjustl(scheme_lower)) + + ! Set order + if (present(order)) then + this%spatial_order = order + else + ! Smart defaults + if (index(this%recon_scheme, 'weno') > 0) then + this%spatial_order = 5 + else if (trim(this%recon_scheme) == 'eno') then + this%spatial_order = 3 + else + print *, "[ERROR] Unsupported reconstruction scheme: ", trim(this%recon_scheme) + return + end if + end if + + if (this%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(this%recon_scheme), & + " Order: ", this%spatial_order + end if + end subroutine config_with_reconstruction + + subroutine to_lower_inplace(str) + character(len=*), intent(inout) :: str + integer :: i + + do i = 1, len_trim(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + str(i:i) = char(ichar(str(i:i)) + 32) + end if + end do + end subroutine to_lower_inplace + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/01l/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..896969bd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/src/infrastructure/mesh.f90 @@ -0,0 +1,74 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, mesh_type, mesh_init, mesh_print_info + + ! mesh + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer :: ncells = 40 + integer :: nnodes + integer :: nx + real(wp), allocatable :: x(:) ! + real(wp), allocatable :: xcc(:) ! + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer, optional, intent(in) :: ncells + + integer :: i + + ! Set + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! computation + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! node + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! cell + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== ===" + print *, ": [", this%xmin, ", ", this%xmax, "]" + print *, ": ", this%ncells + print *, ": ", this%nnodes + print *, " dx: ", this%dx + print *, " L: ", this%L + print *, "==========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01l/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01l/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..3fde7746 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,28 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux STATIC + base.f90 + rusanov.f90 +) + +#target_link_libraries(flux PRIVATE core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 明确设置不使用 /Qm64 选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + set_target_properties(flux PROPERTIES + Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /Qm64" # 明确设置,避免继承问题 + ) +endif() + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/01l/src/numerics/flux/base.f90 new file mode 100644 index 00000000..7080a7ae --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/src/numerics/flux/base.f90 @@ -0,0 +1,30 @@ +!src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + procedure :: print_basic_info => flux_print_basic ! 添加辅助方法 + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + + subroutine flux_print_basic(this) + class(flux_calculator_base), intent(in) :: this + print *, " Name: ", trim(this%name) + end subroutine flux_print_basic + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/01l/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..7140f710 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/src/numerics/flux/rusanov.f90 @@ -0,0 +1,41 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + + ! 构造函数接口 + interface rusanov_flux + module procedure create_rusanov_flux + end interface + +contains + + ! 构造函数 + type(rusanov_flux) function create_rusanov_flux() result(this) + this%name = "Rusanov" + this%wave_speed_default = 1.0_real64 + end function create_rusanov_flux + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Flux calculator information:" + call this%print_basic_info() + + ! 添加Rusanov特有信息 + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01l/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..5e4b938d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,22 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor STATIC + base.f90 + eno.f90 + weno3.f90 +) + +target_link_libraries(reconstructor PRIVATE core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/01l/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..53798d02 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/src/numerics/reconstructor/base.f90 @@ -0,0 +1,33 @@ +!src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + procedure :: print_basic_info => reconstructor_print_basic ! 添加一个辅助方法 + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + + subroutine reconstructor_print_basic(this) + class(reconstructor_base), intent(in) :: this + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_print_basic + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/01l/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..a468b82e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + + ! 构造函数接口 + interface eno_reconstructor + module procedure create_eno_reconstructor + end interface + +contains + + ! 构造函数 + type(eno_reconstructor) function create_eno_reconstructor() result(this) + this%name = "ENO" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_eno_reconstructor + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加ENO特有信息 + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/01l/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..5e954291 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno3_reconstructor + + type, extends(reconstructor_base) :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + + ! 构造函数接口 + interface weno3_reconstructor + module procedure create_weno3_reconstructor + end interface + +contains + + ! 构造函数 + type(weno3_reconstructor) function create_weno3_reconstructor() result(this) + this%name = "WENO3" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_weno3_reconstructor + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO3特有信息 + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01l/tests/CMakeLists.txt new file mode 100644 index 00000000..0cd64b0c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/tests/CMakeLists.txt @@ -0,0 +1,43 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +add_executable(test_minimal_simple test_minimal_simple.f90) + +message(STATUS "CMAKE_Fortran_MODULE_DIRECTORY=${CMAKE_Fortran_MODULE_DIRECTORY}") + +#target_include_directories( test_minimal_simple +# PRIVATE +# ${CMAKE_Fortran_MODULE_DIRECTORY} +#) + +target_link_libraries( test_minimal_simple + PRIVATE + infrastructure +) + +add_executable(test_simple_link test_simple_link.f90) +target_link_libraries(test_simple_link + PRIVATE + reconstructor + flux +) + + +#add_executable(test_factory_simple test_factory_simple.f90) + +#target_link_libraries( test_factory_simple +# PRIVATE +# core +# infrastructure +# reconstructor +# flux +#) + +add_executable(test_factory_simple test_factory_simple.f90) +target_link_libraries(test_factory_simple + PRIVATE + core + infrastructure + reconstructor + flux +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/tests/test_factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/01l/tests/test_factory.f90 new file mode 100644 index 00000000..c62d5f8a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/tests/test_factory.f90 @@ -0,0 +1,154 @@ +! tests/test_factory.f90 +program test_factory + use registry_module + use config_module + use mesh_module + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + class(*), allocatable :: instance + class(reconstructor_base), allocatable :: recon + class(flux_calculator_base), allocatable :: flux_calc + real(wp), allocatable :: q(:), qL(:), qR(:), flux(:) + integer :: i, n + + print *, "=== Factory Pattern Test ===" + print *, "" + + ! Initialize systems + call initialize_registry(verbose=.true.) + + ! Register components with factories + print *, "1. Registering components with factories..." + call register_component_with_factory("reconstructor", "eno", create_eno, 3) + call register_component_with_factory("reconstructor", "weno3", create_weno3, 3) + call register_component_with_factory("flux", "rusanov", create_rusanov) + + call component_registry%list_all() + print *, "" + + ! Test creating ENO reconstructor + print *, "2. Creating ENO reconstructor..." + call create_component("reconstructor", "eno", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (eno_reconstructor) + allocate(recon, source=inst) + print *, "ENO reconstructor created successfully" + call recon%info() + print *, "" + + ! Test reconstruction + n = 10 + allocate(q(0:n+1), qL(n), qR(n)) + + ! Initialize test data (sine wave) + do i = 0, n+1 + q(i) = sin(2.0_wp * 3.141592653589793_wp * real(i-1, wp) / real(n, wp)) + end do + + print *, "Testing ENO reconstruction..." + call recon%reconstruct(q, qL, qR) + + print *, "q (internal):" + do i = 1, n + write(*, '(I3, F10.6)') i, q(i) + end do + + print *, "qL (left interface values):" + do i = 1, n + write(*, '(I3, F10.6)') i, qL(i) + end do + + deallocate(q, qL, qR) + class default + print *, "[ERROR] Wrong type for ENO reconstructor" + end select + else + print *, "[ERROR] Failed to create ENO reconstructor" + end if + print *, "" + + ! Test creating WENO3 reconstructor + print *, "3. Creating WENO3 reconstructor..." + if (allocated(instance)) deallocate(instance) + call create_component("reconstructor", "weno3", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (weno3_reconstructor) + if (allocated(recon)) deallocate(recon) + allocate(recon, source=inst) + print *, "WENO3 reconstructor created successfully" + call recon%info() + class default + print *, "[ERROR] Wrong type for WENO3 reconstructor" + end select + else + print *, "[ERROR] Failed to create WENO3 reconstructor" + end if + print *, "" + + ! Test creating Rusanov flux calculator + print *, "4. Creating Rusanov flux calculator..." + if (allocated(instance)) deallocate(instance) + call create_component("flux", "rusanov", instance) + + if (allocated(instance)) then + select type (inst => instance) + type is (rusanov_flux) + allocate(flux_calc, source=inst) + print *, "Rusanov flux calculator created successfully" + call flux_calc%info() + print *, "" + + ! Test flux computation + n = 5 + allocate(qL(n), qR(n), flux(n)) + + ! Initialize test data + do i = 1, n + qL(i) = 1.0_wp + 0.1_wp * real(i-1, wp) + qR(i) = 1.0_wp + 0.1_wp * real(i, wp) + end do + + print *, "Testing Rusanov flux computation..." + call flux_calc%compute(qL, qR, flux, 1.0_wp) + + print *, "qL:" + do i = 1, n + write(*, '(I3, F10.6)') i, qL(i) + end do + + print *, "qR:" + do i = 1, n + write(*, '(I3, F10.6)') i, qR(i) + end do + + print *, "Flux:" + do i = 1, n + write(*, '(I3, F10.6)') i, flux(i) + end do + + deallocate(qL, qR, flux) + class default + print *, "[ERROR] Wrong type for Rusanov flux" + end select + else + print *, "[ERROR] Failed to create Rusanov flux calculator" + end if + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Factory pattern test completed successfully ===" + +end program test_factory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/tests/test_factory_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/01l/tests/test_factory_simple.f90 new file mode 100644 index 00000000..d4139bb1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/tests/test_factory_simple.f90 @@ -0,0 +1,86 @@ +!tests/test_factory_simple.f90 +program test_factory_simple + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module, only: initialize_registry, cleanup_registry, & + register_component_simple, has_component + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Basic systems + print *, "1. Testing basic systems..." + print *, "-----------------------------" + call config_print(config) + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Creating reconstructors + print *, "2. Testing reconstructors..." + print *, "------------------------------" + + ! 创建并测试ENO重构器 + print *, "Creating ENO reconstructor..." + eno = eno_reconstructor() ! 使用构造函数 + call eno%info() ! 必须调用info方法 + + print *, "" + print *, "Creating WENO3 reconstructor..." + weno3 = weno3_reconstructor() ! 使用构造函数 + call weno3%info() ! 必须调用info方法 + print *, "" + + ! Test 3: Creating flux calculator + print *, "3. Testing flux calculator..." + print *, "-------------------------------" + + print *, "Creating Rusanov flux calculator..." + rusanov = rusanov_flux() ! 使用构造函数 + call rusanov%info() ! 必须调用info方法 + print *, "" + + ! Test 4: Registry integration + print *, "4. Testing registry..." + print *, "----------------------" + + call initialize_registry(verbose=.true.) + + ! Register components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! Check registration + if (has_component("reconstructor", "eno")) then + print *, "[OK] ENO reconstructor registered successfully" + end if + + if (has_component("reconstructor", "weno3")) then + print *, "[OK] WENO3 reconstructor registered successfully" + end if + + if (has_component("flux", "rusanov")) then + print *, "[OK] Rusanov flux registered successfully" + end if + + print *, "" + call cleanup_registry() + + print *, "=== Factory pattern simple test completed successfully ===" + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/tests/test_minimal.f90 b/example/1d-linear-convection/weno3/fortran/registry/01l/tests/test_minimal.f90 new file mode 100644 index 00000000..807d0bc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/tests/test_minimal.f90 @@ -0,0 +1,108 @@ +! tests/test_minimal.f90 +program test_minimal + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i ! Declare loop variable here + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", component_registry%size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components + print *, "5. Testing available components" + print *, "--------------------------------" + + ! Use a separate block to avoid allocation issues + call test_available_components() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Minimal test completed successfully ===" + +contains + + ! Internal subroutine for testing available components + subroutine test_available_components() + character(len=:), allocatable :: names(:) + integer, allocatable :: orders(:) + integer :: j + + call get_available_components("reconstructor", names, orders) + + if (allocated(names)) then + print *, "Reconstructors:" + do j = 1, size(names) + print *, " - ", trim(names(j)) + if (allocated(orders)) then + print *, " Order: ", orders(j) + end if + end do + else + print *, "No reconstructors found" + end if + end subroutine test_available_components + +end program test_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/tests/test_minimal_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/01l/tests/test_minimal_simple.f90 new file mode 100644 index 00000000..6213e8f8 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/tests/test_minimal_simple.f90 @@ -0,0 +1,84 @@ +! tests/test_minimal_simple.f90 +program test_minimal_simple + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Simple Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components (simple version) + print *, "5. Testing registry functionality" + print *, "---------------------------------" + print *, "Registry initialized: ", registry_is_initialized() + print *, "Number of components: ", registry_get_size() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Simple test completed successfully ===" + +end program test_minimal_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01l/tests/test_simple_link.f90 b/example/1d-linear-convection/weno3/fortran/registry/01l/tests/test_simple_link.f90 new file mode 100644 index 00000000..807d0bc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01l/tests/test_simple_link.f90 @@ -0,0 +1,108 @@ +! tests/test_minimal.f90 +program test_minimal + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i ! Declare loop variable here + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", component_registry%size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components + print *, "5. Testing available components" + print *, "--------------------------------" + + ! Use a separate block to avoid allocation issues + call test_available_components() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Minimal test completed successfully ===" + +contains + + ! Internal subroutine for testing available components + subroutine test_available_components() + character(len=:), allocatable :: names(:) + integer, allocatable :: orders(:) + integer :: j + + call get_available_components("reconstructor", names, orders) + + if (allocated(names)) then + print *, "Reconstructors:" + do j = 1, size(names) + print *, " - ", trim(names(j)) + if (allocated(orders)) then + print *, " Order: ", orders(j) + end if + end do + else + print *, "No reconstructors found" + end if + end subroutine test_available_components + +end program test_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01m/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01m/CMakeLists.txt new file mode 100644 index 00000000..ef66d584 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01m/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01m/README.md b/example/1d-linear-convection/weno3/fortran/registry/01m/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01m/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01m/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/01m/scripts/build.bat new file mode 100644 index 00000000..6fd6dc03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01m/scripts/build.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Project Builder +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python build script with full Intel environment support... +echo. + +python build.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Build failed + pause + exit /b 1 +) + +echo. +echo [INFO] Build completed successfully! +echo. +echo [INFO] To run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01m/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/01m/scripts/build.py new file mode 100644 index 00000000..3bf6d537 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01m/scripts/build.py @@ -0,0 +1,629 @@ +#!/usr/bin/env python3 +""" +Fortran CFD Project Builder - 完整Python解决方案 +在Python内部处理Intel oneAPI环境配置 +""" + +import os +import sys +import subprocess +import shutil +import argparse +import time +import platform +import tempfile +from pathlib import Path + +class IntelEnvironment: + """Intel oneAPI环境管理器""" + + def __init__(self): + self.setvars_path = None + self.env_vars = {} + + def find_setvars(self): + """查找setvars.bat文件""" + possible_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + r"C:\Program Files (x86)\Intel\oneAPI\compiler\latest\env\vars.bat", + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\setvars.bat"), + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\compiler\latest\env\vars.bat"), + ] + + for path in possible_paths: + if os.path.exists(path): + self.setvars_path = path + return True + + return False + + def setup_environment(self): + """设置Intel环境""" + if not self.find_setvars(): + return False + + try: + # 创建临时的批处理文件来捕获环境变量 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + f.write(f'@echo off\n') + f.write(f'call "{self.setvars_path}" >nul 2>&1\n') + f.write(f'set\n') # 输出所有环境变量 + temp_bat = f.name + + # 运行批处理文件并捕获输出 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + shell=True + ) + + # 解析环境变量 + for line in result.stdout.split('\n'): + line = line.strip() + if '=' in line: + key, value = line.split('=', 1) + self.env_vars[key.strip()] = value.strip() + + # 清理临时文件 + os.unlink(temp_bat) + + # 更新当前进程的环境变量 + os.environ.update(self.env_vars) + + return True + + except Exception as e: + print(f"设置Intel环境失败: {e}") + return False + + def get_compiler_info(self): + """获取编译器信息""" + info = {} + + # 检查ifx编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + env={**os.environ, **self.env_vars} if self.env_vars else os.environ + ) + + if result.returncode == 0: + for line in result.stdout.split('\n'): + if 'Version' in line or '版本' in line: + info['ifx_version'] = line.strip() + break + except: + pass + + # 检查环境变量 + info['ifx_root'] = self.env_vars.get('IFX_ROOT', '') + info['compiler_root'] = self.env_vars.get('ONEAPI_ROOT', '') + + return info + +class BuildSystem: + """构建系统主类""" + + def __init__(self): + self.project_root = Path(__file__).parent.parent + self.build_dir = self.project_root / "build" + self.intel_env = IntelEnvironment() + + # 设置控制台编码 + if sys.platform == "win32": + try: + import ctypes + # 设置控制台输出为UTF-8 + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + except: + pass + + def print_header(self, text): + """打印标题""" + print(f"\n{'='*70}") + print(f" {text}") + print(f"{'='*70}\n") + + def print_step(self, step, total, message): + """打印步骤""" + print(f"[{step}/{total}] {message}...") + + def print_success(self, message): + """打印成功""" + print(f"\033[92m✓ {message}\033[0m") + + def print_error(self, message): + """打印错误""" + print(f"\033[91m✗ {message}\033[0m") + + def print_warning(self, message): + """打印警告""" + print(f"\033[93m! {message}\033[0m") + + def print_info(self, message): + """打印信息""" + print(f"\033[94mℹ {message}\033[0m") + + def check_prerequisites(self): + """检查前提条件""" + self.print_step(1, 6, "检查前提条件") + + # 检查Python版本 + python_version = sys.version.split()[0] + self.print_info(f"Python版本: {python_version}") + + # 检查平台 + self.print_info(f"平台: {platform.system()} {platform.release()}") + self.print_info(f"处理器核心数: {os.cpu_count()}") + + # 检查CMake + try: + result = subprocess.run( + ["cmake", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + version_line = result.stdout.split('\n')[0] + self.print_success(f"CMake: {version_line}") + else: + self.print_error("CMake未找到") + return False + except FileNotFoundError: + self.print_error("CMake未安装") + return False + + return True + + def setup_intel_environment(self, args): + """设置Intel环境""" + self.print_step(2, 6, "配置Intel oneAPI环境") + + if not self.intel_env.find_setvars(): + self.print_warning("未找到Intel oneAPI setvars.bat") + self.print_info("将尝试使用系统环境中的编译器") + + # 检查是否能直接访问编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + self.print_success("Intel编译器在系统PATH中找到") + return True + else: + self.print_warning("Intel编译器未在PATH中找到") + except: + self.print_warning("无法访问Intel编译器") + + return True # 继续,让CMake自己找编译器 + + # 设置环境 + if self.intel_env.setup_environment(): + compiler_info = self.intel_env.get_compiler_info() + + if compiler_info.get('ifx_version'): + self.print_success(f"Intel Fortran编译器: {compiler_info['ifx_version']}") + elif compiler_info.get('ifx_root'): + self.print_success(f"Intel编译器路径: {compiler_info['ifx_root']}") + else: + self.print_success("Intel oneAPI环境配置完成") + + return True + else: + self.print_warning("Intel环境配置失败,将继续使用系统环境") + return True + + def clean_build_directory(self, args): + """清理构建目录""" + if args.clean and self.build_dir.exists(): + self.print_info("清理构建目录...") + try: + shutil.rmtree(self.build_dir) + self.print_success("构建目录已清理") + except Exception as e: + self.print_error(f"清理失败: {e}") + if not args.force: + return False + return True + + def run_command(self, cmd, cwd=None, check=True, env=None): + """运行命令""" + if isinstance(cmd, list): + cmd_str = ' '.join(str(c) for c in cmd if c) + else: + cmd_str = str(cmd) + + print(f" \033[96m$\033[0m {cmd_str}") + + try: + # 合并环境变量 + exec_env = os.environ.copy() + if env: + exec_env.update(env) + if self.intel_env.env_vars: + exec_env.update(self.intel_env.env_vars) + + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=False, + env=exec_env + ) + + # 处理输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or '完成' in line or '生成' in line: + print(f" \033[92m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + if check and result.returncode != 0: + self.print_error(f"命令执行失败,退出码: {result.returncode}") + return False + + return True + + except Exception as e: + self.print_error(f"命令执行异常: {e}") + return False + + def configure_cmake(self, args): + """配置CMake""" + self.print_step(3, 6, "配置CMake项目") + + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ] + + if args.compiler == "ifx": + cmake_cmd.extend(["-T", "fortran=ifx"]) + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + success = self.run_command(cmake_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("CMake配置完成") + else: + self.print_error("CMake配置失败") + + return success + + def build_project(self, args): + """构建项目""" + self.print_step(4, 6, "构建项目") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + success = self.run_command(build_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("项目构建完成") + else: + self.print_error("构建失败") + + return success + + def run_tests_with_environment(self, test_exe): + """运行单个测试,确保有Intel环境""" + try: + # 创建临时的批处理文件来运行测试 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'"{test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'"{test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + return result + + except Exception as e: + print(f"运行测试失败: {e}") + return None + + def run_tests(self, args): + """运行测试""" + self.print_step(5, 6, "运行测试") + + # 查找测试可执行文件 + test_dir = self.build_dir / "bin" / args.build_type + if not test_dir.exists(): + test_dir = self.build_dir / "bin" + if not test_dir.exists(): + test_dir = self.build_dir + + test_files = list(test_dir.glob("test_*.exe")) + + if not test_files: + self.print_warning("未找到测试程序") + return True + + all_passed = True + + for test_exe in sorted(test_files): + test_name = test_exe.stem + self.print_info(f"运行测试: {test_name}") + print(f" {'-'*50}") + + # 运行测试 + result = self.run_tests_with_environment(str(test_exe)) + + if result is None: + self.print_error(f" {test_name} 运行失败") + all_passed = False + continue + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + print(f" {line}") + + if result.returncode == 0: + self.print_success(f" {test_name} 通过") + else: + self.print_error(f" {test_name} 失败 (退出码: {result.returncode})") + all_passed = False + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + print() # 空行 + + return all_passed + + def create_test_runner(self, args): + """创建独立的测试运行器""" + self.print_step(6, 6, "创建测试运行器") + + runner_path = self.build_dir / "run_tests.bat" + + content = f'''@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Test Runner +echo ======================================== +echo. + +REM Setup Intel oneAPI environment +set "SETVARS_PATH={self.intel_env.setvars_path or ''}" +if exist "%SETVARS_PATH%" ( + call "%SETVARS_PATH%" >nul + echo [INFO] Intel environment configured +) else ( + echo [WARNING] Intel environment not found + echo [WARNING] Tests may fail without runtime libraries +) + +echo. + +REM Run all test executables +set "TEST_COUNT=0" +set "PASS_COUNT=0" + +for %%f in ("bin\\{args.build_type}\\test_*.exe") do ( + set /a TEST_COUNT+=1 + echo [TEST %%f] + echo {'-'*50} + + %%f + if errorlevel 1 ( + echo [FAILED] %%f + ) else ( + echo [PASSED] %%f + set /a PASS_COUNT+=1 + ) + echo. +) + +echo ======================================== +echo Tests: %PASS_COUNT%/%TEST_COUNT% passed +if %PASS_COUNT% equ %TEST_COUNT% ( + echo [SUCCESS] All tests passed! +) else ( + echo [FAILURE] Some tests failed +) +echo ======================================== + +pause +''' + + with open(runner_path, 'w', encoding='utf-8') as f: + f.write(content) + + self.print_success(f"测试运行器已创建: {runner_path}") + self.print_info(f"使用方法: cd build && run_tests.bat") + + return runner_path + + def generate_report(self, args, build_time, tests_passed): + """生成构建报告""" + self.print_header("构建完成") + + print(f"项目: {self.project_root.name}") + print(f"构建类型: {args.build_type}") + print(f"编译器: {args.compiler}") + print(f"并行作业: {args.jobs}") + print(f"总耗时: {build_time:.1f}秒") + print(f"测试结果: {'全部通过' if tests_passed else '有失败'}") + + # 显示生成的可执行文件 + bin_dir = self.build_dir / "bin" / args.build_type + if bin_dir.exists(): + print(f"\n生成的可执行文件:") + for exe in sorted(bin_dir.glob("*.exe")): + size_mb = exe.stat().st_size / (1024 * 1024) + print(f" • {exe.name} ({size_mb:.2f} MB)") + + # 显示测试运行器信息 + runner_path = self.build_dir / "run_tests.bat" + if runner_path.exists(): + print(f"\n独立测试运行器:") + print(f" • {runner_path.name}") + print(f" 在Intel oneAPI环境中运行所有测试") + + def run(self): + """运行构建系统""" + parser = argparse.ArgumentParser( + description="Fortran CFD项目构建工具", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认构建 + %(prog)s --clean # 清理后构建 + %(prog)s --build-type Release # Release构建 + %(prog)s --no-tests # 只构建,不运行测试 + %(prog)s -j8 --verbose # 8线程并行构建,详细输出 + """ + ) + + parser.add_argument("--build-type", choices=["Debug", "Release"], + default="Debug", help="构建类型") + parser.add_argument("--compiler", choices=["ifx", "ifort"], + default="ifx", help="Fortran编译器") + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-tests", action="store_true", + help="跳过测试") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + + args = parser.parse_args() + + # 开始构建 + start_time = time.time() + + self.print_header("Fortran CFD 项目构建系统") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 检查前提条件 + if not self.check_prerequisites(): + if not args.force: + return 1 + + # 2. 设置Intel环境 + if not self.setup_intel_environment(args): + if not args.force: + return 1 + + # 3. 清理目录 + if not self.clean_build_directory(args): + if not args.force: + return 1 + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 4. 配置CMake + if not self.configure_cmake(args): + if not args.force: + return 1 + + # 5. 构建项目 + if not self.build_project(args): + if not args.force: + return 1 + + # 6. 运行测试和创建测试运行器 + tests_passed = True + if not args.no_tests: + tests_passed = self.run_tests(args) + + # 创建测试运行器 + self.create_test_runner(args) # 传递 args 参数 + + # 7. 生成报告 + build_time = time.time() - start_time + self.generate_report(args, build_time, tests_passed) + + return 0 if tests_passed else 1 + + except KeyboardInterrupt: + self.print_error("\n构建被用户中断") + return 1 + except Exception as e: + self.print_error(f"构建过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + builder = BuildSystem() + return builder.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01m/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/01m/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01m/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01m/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01m/src/CMakeLists.txt new file mode 100644 index 00000000..ee38952b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01m/src/CMakeLists.txt @@ -0,0 +1,14 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01m/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01m/src/core/CMakeLists.txt new file mode 100644 index 00000000..bcfd024d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01m/src/core/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/core/CMakeLists.txt +message(STATUS "配置核心模块...") + +add_library(core STATIC + registry.f90 + factory_interfaces.f90 +) + +target_include_directories(core PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 安装规则 +install(TARGETS core + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) +install(FILES + ${CMAKE_Fortran_MODULE_DIRECTORY}/registry_module.mod + ${CMAKE_Fortran_MODULE_DIRECTORY}/factory_interfaces.mod + DESTINATION include/fortran_cfd/core +) + +message(STATUS "核心模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01m/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/01m/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..a960167c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01m/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01m/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/01m/src/core/registry.f90 new file mode 100644 index 00000000..bf9028c3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01m/src/core/registry.f90 @@ -0,0 +1,318 @@ +! src/core/registry.f90 +module registry_module + use, intrinsic :: iso_fortran_env, only: real64 + use factory_interfaces, only: factory_procedure + implicit none + + private + + ! Public interface + public :: real64, component_info, component_registry + public :: register_component_simple, initialize_registry, cleanup_registry + public :: has_component, get_available_components + public :: registry_is_initialized, registry_get_size ! + + ! Type definitions (simplified, no factory for now) + type :: component_info + character(len=32) :: category = "" + character(len=32) :: name = "" + integer :: order = 0 + contains + procedure :: print => ci_print + end type component_info + + type :: component_registry_type + private + type(component_info), allocatable :: components(:) + integer :: count = 0 + integer :: capacity = 100 + logical :: verbose = .true. + logical :: initialized = .false. + contains + procedure :: register => cr_register + procedure :: get => cr_get + procedure :: list_all => cr_list_all + procedure :: clear => cr_clear + procedure :: size => cr_size + procedure :: is_initialized => cr_is_initialized ! + end type component_registry_type + + ! Global registry instance + type(component_registry_type), save :: component_registry + +contains + + ! ==================== PUBLIC API ==================== + + ! Initialize registry + subroutine initialize_registry(initial_capacity, verbose) + integer, optional, intent(in) :: initial_capacity + logical, optional, intent(in) :: verbose + + if (component_registry%initialized) then + if (component_registry%verbose) then + print *, "[INFO] Registry already initialized" + end if + return + end if + + if (present(initial_capacity)) then + component_registry%capacity = max(10, initial_capacity) + end if + + if (present(verbose)) then + component_registry%verbose = verbose + end if + + ! Allocate array + allocate(component_registry%components(component_registry%capacity)) + + component_registry%initialized = .true. + component_registry%count = 0 + + if (component_registry%verbose) then + print *, "[INIT] Registry initialized, capacity:", component_registry%capacity + end if + end subroutine initialize_registry + + ! Cleanup registry + subroutine cleanup_registry + call component_registry%clear() + if (component_registry%verbose) then + print *, "[CLEANUP] Registry cleaned up" + end if + end subroutine cleanup_registry + + ! Simple registration (no factory) + subroutine register_component_simple(category, name) + character(len=*), intent(in) :: category, name + + type(component_info) :: info + + info%category = to_lower(trim(adjustl(category))) + info%name = to_lower(trim(adjustl(name))) + info%order = 0 + + call component_registry%register(info) + end subroutine register_component_simple + + ! Check if component exists + function has_component(category, name) result(found) + character(len=*), intent(in) :: category, name + logical :: found + + type(component_info) :: info + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + info = component_registry%get(cat_lower, name_lower) + found = (len_trim(info%category) > 0) + end function has_component + + ! Get available components in a category + subroutine get_available_components(category, names, orders) + character(len=*), intent(in) :: category + character(len=:), allocatable, intent(out), optional :: names(:) + integer, allocatable, intent(out), optional :: orders(:) + + character(len=32) :: cat_lower + integer :: i, count, idx + type(component_info) :: info + + cat_lower = to_lower(trim(adjustl(category))) + + ! Count components in this category + count = 0 + do i = 1, component_registry%count + if (component_registry%components(i)%category == cat_lower) then + count = count + 1 + end if + end do + + ! Allocate arrays if requested + if (present(names)) then + allocate(character(len=32) :: names(count)) + end if + + if (present(orders)) then + allocate(orders(count)) + end if + + ! Fill arrays + idx = 1 + do i = 1, component_registry%count + if (component_registry%components(i)%category == cat_lower) then + info = component_registry%components(i) + if (present(names)) then + names(idx) = info%name + end if + if (present(orders)) then + orders(idx) = info%order + end if + idx = idx + 1 + end if + end do + end subroutine get_available_components + + ! Public function to check if registry is initialized + function registry_is_initialized() result(is_initialized) + logical :: is_initialized + is_initialized = component_registry%is_initialized() + end function registry_is_initialized + + ! Public function to get registry size + function registry_get_size() result(size_val) + integer :: size_val + size_val = component_registry%size() + end function registry_get_size + + ! ==================== COMPONENT INFO METHODS ==================== + + subroutine ci_print(this) + class(component_info), intent(in) :: this + + if (this%order > 0) then + print *, " [", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")]" + else + print *, " [", trim(this%category), ".", trim(this%name), "]" + end if + end subroutine ci_print + + ! ==================== REGISTRY INTERNAL METHODS ==================== + + subroutine cr_register(this, info) + class(component_registry_type), intent(inout) :: this + type(component_info), intent(in) :: info + + type(component_info), allocatable :: temp(:) + integer :: i + + if (.not. this%initialized) then + error stop "[ERROR] Registry not initialized, call initialize_registry first" + end if + + ! Check if already exists + do i = 1, this%count + if (this%components(i)%category == info%category .and. & + this%components(i)%name == info%name) then + if (this%verbose) then + print *, "[WARN] Overwriting: ", & + trim(info%category), ".", trim(info%name) + end if + this%components(i) = info + return + end if + end do + + ! Expand array if needed + if (this%count >= this%capacity) then + this%capacity = this%capacity * 2 + allocate(temp(this%capacity)) + temp(1:this%count) = this%components(1:this%count) + call move_alloc(temp, this%components) + + if (this%verbose) then + print *, "[INFO] Registry expanded to capacity:", this%capacity + end if + end if + + ! Add component + this%count = this%count + 1 + this%components(this%count) = info + + if (this%verbose) then + print *, "[OK] Registered: ", trim(info%category), ".", trim(info%name) + end if + end subroutine cr_register + + function cr_get(this, category, name) result(info) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + type(component_info) :: info + + integer :: i + + ! Initialize return value as empty + info%category = "" + info%name = "" + info%order = 0 + + if (.not. this%initialized) then + return + end if + + do i = 1, this%count + if (this%components(i)%category == category .and. & + this%components(i)%name == name) then + info = this%components(i) + return + end if + end do + end function cr_get + + subroutine cr_list_all(this) + class(component_registry_type), intent(in) :: this + integer :: i + + if (.not. this%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + print *, "=== Registry Contents (", this%count, " components) ===" + + if (this%count == 0) then + print *, " (empty)" + return + end if + + ! Show components grouped by category + do i = 1, this%count + call this%components(i)%print() + end do + + print *, "===========================================" + end subroutine cr_list_all + + subroutine cr_clear(this) + class(component_registry_type), intent(inout) :: this + + if (allocated(this%components)) then + deallocate(this%components) + end if + + this%count = 0 + this%capacity = 100 + this%initialized = .false. + end subroutine cr_clear + + integer function cr_size(this) + class(component_registry_type), intent(in) :: this + cr_size = this%count + end function cr_size + + logical function cr_is_initialized(this) + class(component_registry_type), intent(in) :: this + cr_is_initialized = this%initialized + end function cr_is_initialized + + ! ==================== UTILITY FUNCTIONS ==================== + + function to_lower(str) result(lower_str) + character(len=*), intent(in) :: str + character(len=len(str)) :: lower_str + integer :: i + + do i = 1, len(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + lower_str(i:i) = char(ichar(str(i:i)) + 32) + else + lower_str(i:i) = str(i:i) + end if + end do + end function to_lower + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01m/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01m/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..46964ce7 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01m/src/infrastructure/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "配置基础设施模块...") + +add_library(infrastructure STATIC + config.f90 + mesh.f90 +) + +target_link_libraries(infrastructure PRIVATE core) + +target_include_directories(infrastructure PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS infrastructure + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "基础设施模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01m/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/01m/src/infrastructure/config.f90 new file mode 100644 index 00000000..d3b1e0df --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01m/src/infrastructure/config.f90 @@ -0,0 +1,90 @@ +! src/infrastructure/config.f90 +module config_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, cfd_config, config_print, config_with_reconstruction + + ! CFD configuration type + type :: cfd_config + character(len=20) :: ic_type = "step" + character(len=20) :: recon_scheme = "eno" + character(len=20) :: flux_type = "rusanov" + integer :: rk_order = 1 + real(real64) :: wave_speed = 1.0_real64 + real(real64) :: final_time = 0.625_real64 + real(real64) :: dt = 0.025_real64 + character(len=20) :: boundary_type = "periodic" + real(real64) :: left_boundary_value = 1.0_real64 + real(real64) :: right_boundary_value = 2.0_real64 + integer :: spatial_order = 2 + logical :: verbose = .true. + end type cfd_config + +contains + + subroutine config_print(this) + type(cfd_config), intent(in) :: this + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(this%ic_type) + print *, "Reconstruction: ", trim(this%recon_scheme), " (order:", this%spatial_order, ")" + print *, "Flux type: ", trim(this%flux_type) + print *, "Time integration: RK", this%rk_order + print *, "Wave speed: ", this%wave_speed + print *, "Final time: ", this%final_time + print *, "Time step: ", this%dt + print *, "Boundary: ", trim(this%boundary_type) + if (trim(this%boundary_type) == 'dirichlet') then + print *, " Dirichlet values: [", this%left_boundary_value, ", ", & + this%right_boundary_value, "]" + end if + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(this, scheme, order) + type(cfd_config), intent(inout) :: this + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + character(len=20) :: scheme_lower + + ! Convert to lowercase + scheme_lower = scheme + call to_lower_inplace(scheme_lower) + this%recon_scheme = trim(adjustl(scheme_lower)) + + ! Set order + if (present(order)) then + this%spatial_order = order + else + ! Smart defaults + if (index(this%recon_scheme, 'weno') > 0) then + this%spatial_order = 5 + else if (trim(this%recon_scheme) == 'eno') then + this%spatial_order = 3 + else + print *, "[ERROR] Unsupported reconstruction scheme: ", trim(this%recon_scheme) + return + end if + end if + + if (this%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(this%recon_scheme), & + " Order: ", this%spatial_order + end if + end subroutine config_with_reconstruction + + subroutine to_lower_inplace(str) + character(len=*), intent(inout) :: str + integer :: i + + do i = 1, len_trim(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + str(i:i) = char(ichar(str(i:i)) + 32) + end if + end do + end subroutine to_lower_inplace + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01m/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/01m/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..896969bd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01m/src/infrastructure/mesh.f90 @@ -0,0 +1,74 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, mesh_type, mesh_init, mesh_print_info + + ! mesh + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer :: ncells = 40 + integer :: nnodes + integer :: nx + real(wp), allocatable :: x(:) ! + real(wp), allocatable :: xcc(:) ! + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer, optional, intent(in) :: ncells + + integer :: i + + ! Set + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! computation + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! node + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! cell + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== ===" + print *, ": [", this%xmin, ", ", this%xmax, "]" + print *, ": ", this%ncells + print *, ": ", this%nnodes + print *, " dx: ", this%dx + print *, " L: ", this%L + print *, "==========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01m/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01m/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01m/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01m/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01m/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..3fde7746 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01m/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,28 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux STATIC + base.f90 + rusanov.f90 +) + +#target_link_libraries(flux PRIVATE core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 明确设置不使用 /Qm64 选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + set_target_properties(flux PROPERTIES + Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /Qm64" # 明确设置,避免继承问题 + ) +endif() + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01m/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/01m/src/numerics/flux/base.f90 new file mode 100644 index 00000000..7080a7ae --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01m/src/numerics/flux/base.f90 @@ -0,0 +1,30 @@ +!src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + procedure :: print_basic_info => flux_print_basic ! 添加辅助方法 + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + + subroutine flux_print_basic(this) + class(flux_calculator_base), intent(in) :: this + print *, " Name: ", trim(this%name) + end subroutine flux_print_basic + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01m/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/01m/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..7140f710 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01m/src/numerics/flux/rusanov.f90 @@ -0,0 +1,41 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + + ! 构造函数接口 + interface rusanov_flux + module procedure create_rusanov_flux + end interface + +contains + + ! 构造函数 + type(rusanov_flux) function create_rusanov_flux() result(this) + this%name = "Rusanov" + this%wave_speed_default = 1.0_real64 + end function create_rusanov_flux + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Flux calculator information:" + call this%print_basic_info() + + ! 添加Rusanov特有信息 + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01m/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01m/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..5e4b938d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01m/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,22 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor STATIC + base.f90 + eno.f90 + weno3.f90 +) + +target_link_libraries(reconstructor PRIVATE core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01m/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/01m/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..53798d02 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01m/src/numerics/reconstructor/base.f90 @@ -0,0 +1,33 @@ +!src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + procedure :: print_basic_info => reconstructor_print_basic ! 添加一个辅助方法 + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + + subroutine reconstructor_print_basic(this) + class(reconstructor_base), intent(in) :: this + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_print_basic + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01m/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/01m/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..a468b82e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01m/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + + ! 构造函数接口 + interface eno_reconstructor + module procedure create_eno_reconstructor + end interface + +contains + + ! 构造函数 + type(eno_reconstructor) function create_eno_reconstructor() result(this) + this%name = "ENO" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_eno_reconstructor + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加ENO特有信息 + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01m/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/01m/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..5e954291 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01m/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno3_reconstructor + + type, extends(reconstructor_base) :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + + ! 构造函数接口 + interface weno3_reconstructor + module procedure create_weno3_reconstructor + end interface + +contains + + ! 构造函数 + type(weno3_reconstructor) function create_weno3_reconstructor() result(this) + this%name = "WENO3" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_weno3_reconstructor + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO3特有信息 + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01m/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/01m/tests/CMakeLists.txt new file mode 100644 index 00000000..0cd64b0c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01m/tests/CMakeLists.txt @@ -0,0 +1,43 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +add_executable(test_minimal_simple test_minimal_simple.f90) + +message(STATUS "CMAKE_Fortran_MODULE_DIRECTORY=${CMAKE_Fortran_MODULE_DIRECTORY}") + +#target_include_directories( test_minimal_simple +# PRIVATE +# ${CMAKE_Fortran_MODULE_DIRECTORY} +#) + +target_link_libraries( test_minimal_simple + PRIVATE + infrastructure +) + +add_executable(test_simple_link test_simple_link.f90) +target_link_libraries(test_simple_link + PRIVATE + reconstructor + flux +) + + +#add_executable(test_factory_simple test_factory_simple.f90) + +#target_link_libraries( test_factory_simple +# PRIVATE +# core +# infrastructure +# reconstructor +# flux +#) + +add_executable(test_factory_simple test_factory_simple.f90) +target_link_libraries(test_factory_simple + PRIVATE + core + infrastructure + reconstructor + flux +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01m/tests/test_factory_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/01m/tests/test_factory_simple.f90 new file mode 100644 index 00000000..d4139bb1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01m/tests/test_factory_simple.f90 @@ -0,0 +1,86 @@ +!tests/test_factory_simple.f90 +program test_factory_simple + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module, only: initialize_registry, cleanup_registry, & + register_component_simple, has_component + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Basic systems + print *, "1. Testing basic systems..." + print *, "-----------------------------" + call config_print(config) + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Creating reconstructors + print *, "2. Testing reconstructors..." + print *, "------------------------------" + + ! 创建并测试ENO重构器 + print *, "Creating ENO reconstructor..." + eno = eno_reconstructor() ! 使用构造函数 + call eno%info() ! 必须调用info方法 + + print *, "" + print *, "Creating WENO3 reconstructor..." + weno3 = weno3_reconstructor() ! 使用构造函数 + call weno3%info() ! 必须调用info方法 + print *, "" + + ! Test 3: Creating flux calculator + print *, "3. Testing flux calculator..." + print *, "-------------------------------" + + print *, "Creating Rusanov flux calculator..." + rusanov = rusanov_flux() ! 使用构造函数 + call rusanov%info() ! 必须调用info方法 + print *, "" + + ! Test 4: Registry integration + print *, "4. Testing registry..." + print *, "----------------------" + + call initialize_registry(verbose=.true.) + + ! Register components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! Check registration + if (has_component("reconstructor", "eno")) then + print *, "[OK] ENO reconstructor registered successfully" + end if + + if (has_component("reconstructor", "weno3")) then + print *, "[OK] WENO3 reconstructor registered successfully" + end if + + if (has_component("flux", "rusanov")) then + print *, "[OK] Rusanov flux registered successfully" + end if + + print *, "" + call cleanup_registry() + + print *, "=== Factory pattern simple test completed successfully ===" + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01m/tests/test_minimal_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/01m/tests/test_minimal_simple.f90 new file mode 100644 index 00000000..6213e8f8 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01m/tests/test_minimal_simple.f90 @@ -0,0 +1,84 @@ +! tests/test_minimal_simple.f90 +program test_minimal_simple + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Simple Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components (simple version) + print *, "5. Testing registry functionality" + print *, "---------------------------------" + print *, "Registry initialized: ", registry_is_initialized() + print *, "Number of components: ", registry_get_size() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Simple test completed successfully ===" + +end program test_minimal_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/01m/tests/test_simple_link.f90 b/example/1d-linear-convection/weno3/fortran/registry/01m/tests/test_simple_link.f90 new file mode 100644 index 00000000..807d0bc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/01m/tests/test_simple_link.f90 @@ -0,0 +1,108 @@ +! tests/test_minimal.f90 +program test_minimal + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i ! Declare loop variable here + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", component_registry%size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components + print *, "5. Testing available components" + print *, "--------------------------------" + + ! Use a separate block to avoid allocation issues + call test_available_components() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Minimal test completed successfully ===" + +contains + + ! Internal subroutine for testing available components + subroutine test_available_components() + character(len=:), allocatable :: names(:) + integer, allocatable :: orders(:) + integer :: j + + call get_available_components("reconstructor", names, orders) + + if (allocated(names)) then + print *, "Reconstructors:" + do j = 1, size(names) + print *, " - ", trim(names(j)) + if (allocated(orders)) then + print *, " Order: ", orders(j) + end if + end do + else + print *, "No reconstructors found" + end if + end subroutine test_available_components + +end program test_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02/CMakeLists.txt new file mode 100644 index 00000000..ef66d584 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02/README.md b/example/1d-linear-convection/weno3/fortran/registry/02/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/02/scripts/build.bat new file mode 100644 index 00000000..6fd6dc03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02/scripts/build.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Project Builder +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python build script with full Intel environment support... +echo. + +python build.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Build failed + pause + exit /b 1 +) + +echo. +echo [INFO] Build completed successfully! +echo. +echo [INFO] To run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/02/scripts/build.py new file mode 100644 index 00000000..3bf6d537 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02/scripts/build.py @@ -0,0 +1,629 @@ +#!/usr/bin/env python3 +""" +Fortran CFD Project Builder - 完整Python解决方案 +在Python内部处理Intel oneAPI环境配置 +""" + +import os +import sys +import subprocess +import shutil +import argparse +import time +import platform +import tempfile +from pathlib import Path + +class IntelEnvironment: + """Intel oneAPI环境管理器""" + + def __init__(self): + self.setvars_path = None + self.env_vars = {} + + def find_setvars(self): + """查找setvars.bat文件""" + possible_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + r"C:\Program Files (x86)\Intel\oneAPI\compiler\latest\env\vars.bat", + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\setvars.bat"), + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\compiler\latest\env\vars.bat"), + ] + + for path in possible_paths: + if os.path.exists(path): + self.setvars_path = path + return True + + return False + + def setup_environment(self): + """设置Intel环境""" + if not self.find_setvars(): + return False + + try: + # 创建临时的批处理文件来捕获环境变量 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + f.write(f'@echo off\n') + f.write(f'call "{self.setvars_path}" >nul 2>&1\n') + f.write(f'set\n') # 输出所有环境变量 + temp_bat = f.name + + # 运行批处理文件并捕获输出 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + shell=True + ) + + # 解析环境变量 + for line in result.stdout.split('\n'): + line = line.strip() + if '=' in line: + key, value = line.split('=', 1) + self.env_vars[key.strip()] = value.strip() + + # 清理临时文件 + os.unlink(temp_bat) + + # 更新当前进程的环境变量 + os.environ.update(self.env_vars) + + return True + + except Exception as e: + print(f"设置Intel环境失败: {e}") + return False + + def get_compiler_info(self): + """获取编译器信息""" + info = {} + + # 检查ifx编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + env={**os.environ, **self.env_vars} if self.env_vars else os.environ + ) + + if result.returncode == 0: + for line in result.stdout.split('\n'): + if 'Version' in line or '版本' in line: + info['ifx_version'] = line.strip() + break + except: + pass + + # 检查环境变量 + info['ifx_root'] = self.env_vars.get('IFX_ROOT', '') + info['compiler_root'] = self.env_vars.get('ONEAPI_ROOT', '') + + return info + +class BuildSystem: + """构建系统主类""" + + def __init__(self): + self.project_root = Path(__file__).parent.parent + self.build_dir = self.project_root / "build" + self.intel_env = IntelEnvironment() + + # 设置控制台编码 + if sys.platform == "win32": + try: + import ctypes + # 设置控制台输出为UTF-8 + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + except: + pass + + def print_header(self, text): + """打印标题""" + print(f"\n{'='*70}") + print(f" {text}") + print(f"{'='*70}\n") + + def print_step(self, step, total, message): + """打印步骤""" + print(f"[{step}/{total}] {message}...") + + def print_success(self, message): + """打印成功""" + print(f"\033[92m✓ {message}\033[0m") + + def print_error(self, message): + """打印错误""" + print(f"\033[91m✗ {message}\033[0m") + + def print_warning(self, message): + """打印警告""" + print(f"\033[93m! {message}\033[0m") + + def print_info(self, message): + """打印信息""" + print(f"\033[94mℹ {message}\033[0m") + + def check_prerequisites(self): + """检查前提条件""" + self.print_step(1, 6, "检查前提条件") + + # 检查Python版本 + python_version = sys.version.split()[0] + self.print_info(f"Python版本: {python_version}") + + # 检查平台 + self.print_info(f"平台: {platform.system()} {platform.release()}") + self.print_info(f"处理器核心数: {os.cpu_count()}") + + # 检查CMake + try: + result = subprocess.run( + ["cmake", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + version_line = result.stdout.split('\n')[0] + self.print_success(f"CMake: {version_line}") + else: + self.print_error("CMake未找到") + return False + except FileNotFoundError: + self.print_error("CMake未安装") + return False + + return True + + def setup_intel_environment(self, args): + """设置Intel环境""" + self.print_step(2, 6, "配置Intel oneAPI环境") + + if not self.intel_env.find_setvars(): + self.print_warning("未找到Intel oneAPI setvars.bat") + self.print_info("将尝试使用系统环境中的编译器") + + # 检查是否能直接访问编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + self.print_success("Intel编译器在系统PATH中找到") + return True + else: + self.print_warning("Intel编译器未在PATH中找到") + except: + self.print_warning("无法访问Intel编译器") + + return True # 继续,让CMake自己找编译器 + + # 设置环境 + if self.intel_env.setup_environment(): + compiler_info = self.intel_env.get_compiler_info() + + if compiler_info.get('ifx_version'): + self.print_success(f"Intel Fortran编译器: {compiler_info['ifx_version']}") + elif compiler_info.get('ifx_root'): + self.print_success(f"Intel编译器路径: {compiler_info['ifx_root']}") + else: + self.print_success("Intel oneAPI环境配置完成") + + return True + else: + self.print_warning("Intel环境配置失败,将继续使用系统环境") + return True + + def clean_build_directory(self, args): + """清理构建目录""" + if args.clean and self.build_dir.exists(): + self.print_info("清理构建目录...") + try: + shutil.rmtree(self.build_dir) + self.print_success("构建目录已清理") + except Exception as e: + self.print_error(f"清理失败: {e}") + if not args.force: + return False + return True + + def run_command(self, cmd, cwd=None, check=True, env=None): + """运行命令""" + if isinstance(cmd, list): + cmd_str = ' '.join(str(c) for c in cmd if c) + else: + cmd_str = str(cmd) + + print(f" \033[96m$\033[0m {cmd_str}") + + try: + # 合并环境变量 + exec_env = os.environ.copy() + if env: + exec_env.update(env) + if self.intel_env.env_vars: + exec_env.update(self.intel_env.env_vars) + + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=False, + env=exec_env + ) + + # 处理输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or '完成' in line or '生成' in line: + print(f" \033[92m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + if check and result.returncode != 0: + self.print_error(f"命令执行失败,退出码: {result.returncode}") + return False + + return True + + except Exception as e: + self.print_error(f"命令执行异常: {e}") + return False + + def configure_cmake(self, args): + """配置CMake""" + self.print_step(3, 6, "配置CMake项目") + + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ] + + if args.compiler == "ifx": + cmake_cmd.extend(["-T", "fortran=ifx"]) + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + success = self.run_command(cmake_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("CMake配置完成") + else: + self.print_error("CMake配置失败") + + return success + + def build_project(self, args): + """构建项目""" + self.print_step(4, 6, "构建项目") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + success = self.run_command(build_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("项目构建完成") + else: + self.print_error("构建失败") + + return success + + def run_tests_with_environment(self, test_exe): + """运行单个测试,确保有Intel环境""" + try: + # 创建临时的批处理文件来运行测试 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'"{test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'"{test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + return result + + except Exception as e: + print(f"运行测试失败: {e}") + return None + + def run_tests(self, args): + """运行测试""" + self.print_step(5, 6, "运行测试") + + # 查找测试可执行文件 + test_dir = self.build_dir / "bin" / args.build_type + if not test_dir.exists(): + test_dir = self.build_dir / "bin" + if not test_dir.exists(): + test_dir = self.build_dir + + test_files = list(test_dir.glob("test_*.exe")) + + if not test_files: + self.print_warning("未找到测试程序") + return True + + all_passed = True + + for test_exe in sorted(test_files): + test_name = test_exe.stem + self.print_info(f"运行测试: {test_name}") + print(f" {'-'*50}") + + # 运行测试 + result = self.run_tests_with_environment(str(test_exe)) + + if result is None: + self.print_error(f" {test_name} 运行失败") + all_passed = False + continue + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + print(f" {line}") + + if result.returncode == 0: + self.print_success(f" {test_name} 通过") + else: + self.print_error(f" {test_name} 失败 (退出码: {result.returncode})") + all_passed = False + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + print() # 空行 + + return all_passed + + def create_test_runner(self, args): + """创建独立的测试运行器""" + self.print_step(6, 6, "创建测试运行器") + + runner_path = self.build_dir / "run_tests.bat" + + content = f'''@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Test Runner +echo ======================================== +echo. + +REM Setup Intel oneAPI environment +set "SETVARS_PATH={self.intel_env.setvars_path or ''}" +if exist "%SETVARS_PATH%" ( + call "%SETVARS_PATH%" >nul + echo [INFO] Intel environment configured +) else ( + echo [WARNING] Intel environment not found + echo [WARNING] Tests may fail without runtime libraries +) + +echo. + +REM Run all test executables +set "TEST_COUNT=0" +set "PASS_COUNT=0" + +for %%f in ("bin\\{args.build_type}\\test_*.exe") do ( + set /a TEST_COUNT+=1 + echo [TEST %%f] + echo {'-'*50} + + %%f + if errorlevel 1 ( + echo [FAILED] %%f + ) else ( + echo [PASSED] %%f + set /a PASS_COUNT+=1 + ) + echo. +) + +echo ======================================== +echo Tests: %PASS_COUNT%/%TEST_COUNT% passed +if %PASS_COUNT% equ %TEST_COUNT% ( + echo [SUCCESS] All tests passed! +) else ( + echo [FAILURE] Some tests failed +) +echo ======================================== + +pause +''' + + with open(runner_path, 'w', encoding='utf-8') as f: + f.write(content) + + self.print_success(f"测试运行器已创建: {runner_path}") + self.print_info(f"使用方法: cd build && run_tests.bat") + + return runner_path + + def generate_report(self, args, build_time, tests_passed): + """生成构建报告""" + self.print_header("构建完成") + + print(f"项目: {self.project_root.name}") + print(f"构建类型: {args.build_type}") + print(f"编译器: {args.compiler}") + print(f"并行作业: {args.jobs}") + print(f"总耗时: {build_time:.1f}秒") + print(f"测试结果: {'全部通过' if tests_passed else '有失败'}") + + # 显示生成的可执行文件 + bin_dir = self.build_dir / "bin" / args.build_type + if bin_dir.exists(): + print(f"\n生成的可执行文件:") + for exe in sorted(bin_dir.glob("*.exe")): + size_mb = exe.stat().st_size / (1024 * 1024) + print(f" • {exe.name} ({size_mb:.2f} MB)") + + # 显示测试运行器信息 + runner_path = self.build_dir / "run_tests.bat" + if runner_path.exists(): + print(f"\n独立测试运行器:") + print(f" • {runner_path.name}") + print(f" 在Intel oneAPI环境中运行所有测试") + + def run(self): + """运行构建系统""" + parser = argparse.ArgumentParser( + description="Fortran CFD项目构建工具", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认构建 + %(prog)s --clean # 清理后构建 + %(prog)s --build-type Release # Release构建 + %(prog)s --no-tests # 只构建,不运行测试 + %(prog)s -j8 --verbose # 8线程并行构建,详细输出 + """ + ) + + parser.add_argument("--build-type", choices=["Debug", "Release"], + default="Debug", help="构建类型") + parser.add_argument("--compiler", choices=["ifx", "ifort"], + default="ifx", help="Fortran编译器") + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-tests", action="store_true", + help="跳过测试") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + + args = parser.parse_args() + + # 开始构建 + start_time = time.time() + + self.print_header("Fortran CFD 项目构建系统") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 检查前提条件 + if not self.check_prerequisites(): + if not args.force: + return 1 + + # 2. 设置Intel环境 + if not self.setup_intel_environment(args): + if not args.force: + return 1 + + # 3. 清理目录 + if not self.clean_build_directory(args): + if not args.force: + return 1 + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 4. 配置CMake + if not self.configure_cmake(args): + if not args.force: + return 1 + + # 5. 构建项目 + if not self.build_project(args): + if not args.force: + return 1 + + # 6. 运行测试和创建测试运行器 + tests_passed = True + if not args.no_tests: + tests_passed = self.run_tests(args) + + # 创建测试运行器 + self.create_test_runner(args) # 传递 args 参数 + + # 7. 生成报告 + build_time = time.time() - start_time + self.generate_report(args, build_time, tests_passed) + + return 0 if tests_passed else 1 + + except KeyboardInterrupt: + self.print_error("\n构建被用户中断") + return 1 + except Exception as e: + self.print_error(f"构建过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + builder = BuildSystem() + return builder.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/02/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02/src/CMakeLists.txt new file mode 100644 index 00000000..ee38952b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02/src/CMakeLists.txt @@ -0,0 +1,14 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02/src/core/CMakeLists.txt new file mode 100644 index 00000000..bcfd024d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02/src/core/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/core/CMakeLists.txt +message(STATUS "配置核心模块...") + +add_library(core STATIC + registry.f90 + factory_interfaces.f90 +) + +target_include_directories(core PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 安装规则 +install(TARGETS core + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) +install(FILES + ${CMAKE_Fortran_MODULE_DIRECTORY}/registry_module.mod + ${CMAKE_Fortran_MODULE_DIRECTORY}/factory_interfaces.mod + DESTINATION include/fortran_cfd/core +) + +message(STATUS "核心模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/02/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..a960167c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/02/src/core/registry.f90 new file mode 100644 index 00000000..ada7ca02 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02/src/core/registry.f90 @@ -0,0 +1,668 @@ +! src/core/registry.f90 +module registry_module + use, intrinsic :: iso_fortran_env, only: real64 + use factory_interfaces, only: factory_procedure + implicit none + + private + + ! ==================== PUBLIC INTERFACE ==================== + ! 原有接口保持不变 + public :: real64, component_info, component_registry + public :: register_component_simple, initialize_registry, cleanup_registry + public :: has_component, get_available_components + public :: registry_is_initialized, registry_get_size + public :: list_all ! ← 恢复这个接口 + + ! 新增工厂相关接口 + public :: factory_ptr_type, component_registry_entry + public :: register_component_with_factory + public :: create_component_from_registry + public :: has_factory_component + public :: list_factory_components + + ! ==================== TYPE DEFINITIONS ==================== + + ! 原有类型定义 + type :: component_info + character(len=32) :: category = "" + character(len=32) :: name = "" + integer :: order = 0 + contains + procedure :: print => ci_print + end type component_info + + ! 新增:工厂指针类型 + type :: factory_ptr_type + procedure(factory_procedure), pointer, nopass :: ptr => null() + end type factory_ptr_type + + ! 新增:带工厂的注册条目 + type :: component_registry_entry + character(len=32) :: category = "" + character(len=32) :: name = "" + integer :: order = 0 + type(factory_ptr_type) :: factory + logical :: has_factory = .false. + contains + procedure :: print_with_factory => entry_print + procedure :: can_create => entry_has_factory + end type component_registry_entry + + ! 原有注册表类型(扩展以支持工厂) + type :: component_registry_type + private + ! 简单注册组件(向后兼容) + type(component_info), allocatable :: simple_components(:) + integer :: simple_count = 0 + + ! 带工厂的组件(新增) + type(component_registry_entry), allocatable :: factory_components(:) + integer :: factory_count = 0 + + integer :: capacity = 100 + logical :: verbose = .true. + logical :: initialized = .false. + contains + ! 简单注册方法 + procedure :: register => cr_register ! ← 保持原名 + procedure :: get => cr_get ! ← 保持原名 + procedure :: list_all => cr_list_all ! ← 保持原名(重要!) + procedure :: list_simple => cr_list_simple ! ← 新增别名 + + ! 工厂注册方法 + procedure :: register_with_factory => cr_register_with_factory + procedure :: get_with_factory => cr_get_with_factory + procedure :: list_with_factory => cr_list_with_factory + procedure :: create_from_factory => cr_create_from_factory + + ! 通用方法 + procedure :: clear => cr_clear + procedure :: size => cr_size + procedure :: total_size => cr_total_size + procedure :: is_initialized => cr_is_initialized + end type component_registry_type + + ! Global registry instance + type(component_registry_type), save :: component_registry + +contains + + ! ==================== PUBLIC API ==================== + + ! Initialize registry (保持不变) + subroutine initialize_registry(initial_capacity, verbose) + integer, optional, intent(in) :: initial_capacity + logical, optional, intent(in) :: verbose + + if (component_registry%initialized) then + if (component_registry%verbose) then + print *, "[INFO] Registry already initialized" + end if + return + end if + + if (present(initial_capacity)) then + component_registry%capacity = max(10, initial_capacity) + end if + + if (present(verbose)) then + component_registry%verbose = verbose + end if + + ! Allocate arrays for both types + allocate(component_registry%simple_components(component_registry%capacity)) + allocate(component_registry%factory_components(component_registry%capacity)) + + component_registry%initialized = .true. + component_registry%simple_count = 0 + component_registry%factory_count = 0 + + if (component_registry%verbose) then + print *, "[INIT] Registry initialized, capacity:", component_registry%capacity + print *, " Supports both simple and factory-based registration" + end if + end subroutine initialize_registry + + ! Cleanup registry (保持不变) + subroutine cleanup_registry + call component_registry%clear() + if (component_registry%verbose) then + print *, "[CLEANUP] Registry cleaned up" + end if + end subroutine cleanup_registry + + ! ==================== SIMPLE REGISTRATION (向后兼容) ==================== + + ! Simple registration (no factory) - 保持不变 + subroutine register_component_simple(category, name) + character(len=*), intent(in) :: category, name + + type(component_info) :: info + + info%category = to_lower(trim(adjustl(category))) + info%name = to_lower(trim(adjustl(name))) + info%order = 0 + + call component_registry%register(info) + end subroutine register_component_simple + + ! Check if component exists (简单注册) + function has_component(category, name) result(found) + character(len=*), intent(in) :: category, name + logical :: found + + type(component_info) :: info + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + info = component_registry%get(cat_lower, name_lower) + found = (len_trim(info%category) > 0) + end function has_component + + ! Get available components in a category (简单注册) + subroutine get_available_components(category, names, orders) + character(len=*), intent(in) :: category + character(len=:), allocatable, intent(out), optional :: names(:) + integer, allocatable, intent(out), optional :: orders(:) + + character(len=32) :: cat_lower + integer :: i, count, idx + type(component_info) :: info + + cat_lower = to_lower(trim(adjustl(category))) + + ! Count components in this category + count = 0 + do i = 1, component_registry%simple_count + if (component_registry%simple_components(i)%category == cat_lower) then + count = count + 1 + end if + end do + + ! Allocate arrays if requested + if (present(names)) then + allocate(character(len=32) :: names(count)) + end if + + if (present(orders)) then + allocate(orders(count)) + end if + + ! Fill arrays + idx = 1 + do i = 1, component_registry%simple_count + if (component_registry%simple_components(i)%category == cat_lower) then + info = component_registry%simple_components(i) + if (present(names)) then + names(idx) = info%name + end if + if (present(orders)) then + orders(idx) = info%order + end if + idx = idx + 1 + end if + end do + end subroutine get_available_components + + ! ==================== FACTORY REGISTRATION (新增) ==================== + + ! Register component with factory function + subroutine register_component_with_factory(category, name, factory_func, order) + character(len=*), intent(in) :: category, name + procedure(factory_procedure) :: factory_func + integer, optional, intent(in) :: order + + type(component_registry_entry) :: entry + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + entry%category = cat_lower + entry%name = name_lower + + if (present(order)) then + entry%order = order + else + entry%order = 0 + end if + + entry%factory%ptr => factory_func + entry%has_factory = .true. + + call component_registry%register_with_factory(entry) + end subroutine register_component_with_factory + + ! Create component from registry using factory + function create_component_from_registry(category, name) result(instance) + character(len=*), intent(in) :: category, name + class(*), allocatable :: instance + + character(len=32) :: cat_lower, name_lower + integer :: status + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + call component_registry%create_from_factory(cat_lower, name_lower, instance, status) + + if (status /= 0) then + if (component_registry%verbose) then + print *, "[ERROR] Failed to create component: ", trim(category), ".", trim(name) + end if + end if + end function create_component_from_registry + + ! Check if component has factory + function has_factory_component(category, name) result(found) + character(len=*), intent(in) :: category, name + logical :: found + + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + found = .false. + + if (.not. component_registry%initialized) return + + ! 简化的查找逻辑(实际应调用内部方法) + found = component_registry%factory_count > 0 ! 占位实现 + end function has_factory_component + + ! List all factory components + subroutine list_factory_components(category) + character(len=*), optional, intent(in) :: category + + if (present(category)) then + call component_registry%list_with_factory(category) + else + call component_registry%list_with_factory("") + end if + end subroutine list_factory_components + + ! Public function to list all simple components (向后兼容) + subroutine list_all() + call component_registry%list_all() + end subroutine list_all + + ! ==================== PUBLIC UTILITY FUNCTIONS ==================== + + ! Public function to check if registry is initialized + function registry_is_initialized() result(is_initialized) + logical :: is_initialized + is_initialized = component_registry%is_initialized() + end function registry_is_initialized + + ! Public function to get total registry size + function registry_get_size() result(size_val) + integer :: size_val + size_val = component_registry%total_size() + end function registry_get_size + + ! ==================== COMPONENT INFO METHODS ==================== + + subroutine ci_print(this) + class(component_info), intent(in) :: this + + if (this%order > 0) then + print *, " [", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")]" + else + print *, " [", trim(this%category), ".", trim(this%name), "]" + end if + end subroutine ci_print + + subroutine entry_print(this) + class(component_registry_entry), intent(in) :: this + + if (this%has_factory) then + if (this%order > 0) then + print *, " [FACTORY] ", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")" + else + print *, " [FACTORY] ", trim(this%category), ".", trim(this%name) + end if + else + if (this%order > 0) then + print *, " [SIMPLE] ", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")" + else + print *, " [SIMPLE] ", trim(this%category), ".", trim(this%name) + end if + end if + end subroutine entry_print + + logical function entry_has_factory(this) + class(component_registry_entry), intent(in) :: this + entry_has_factory = this%has_factory + end function entry_has_factory + + ! ==================== REGISTRY INTERNAL METHODS ==================== + + ! ---------- Simple Registration Methods ---------- + + subroutine cr_register(this, info) + class(component_registry_type), intent(inout) :: this + type(component_info), intent(in) :: info + + type(component_info), allocatable :: temp(:) + integer :: i + + if (.not. this%initialized) then + error stop "[ERROR] Registry not initialized, call initialize_registry first" + end if + + ! Check if already exists + do i = 1, this%simple_count + if (this%simple_components(i)%category == info%category .and. & + this%simple_components(i)%name == info%name) then + if (this%verbose) then + print *, "[WARN] Overwriting simple component: ", & + trim(info%category), ".", trim(info%name) + end if + this%simple_components(i) = info + return + end if + end do + + ! Expand array if needed + if (this%simple_count >= this%capacity) then + this%capacity = this%capacity * 2 + allocate(temp(this%capacity)) + temp(1:this%simple_count) = this%simple_components(1:this%simple_count) + call move_alloc(temp, this%simple_components) + + if (this%verbose) then + print *, "[INFO] Simple registry expanded to capacity:", this%capacity + end if + end if + + ! Add component + this%simple_count = this%simple_count + 1 + this%simple_components(this%simple_count) = info + + if (this%verbose) then + print *, "[OK] Registered simple: ", trim(info%category), ".", trim(info%name) + end if + end subroutine cr_register + + function cr_get(this, category, name) result(info) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + type(component_info) :: info + + integer :: i + + ! Initialize return value as empty + info%category = "" + info%name = "" + info%order = 0 + + if (.not. this%initialized) then + return + end if + + do i = 1, this%simple_count + if (this%simple_components(i)%category == category .and. & + this%simple_components(i)%name == name) then + info = this%simple_components(i) + return + end if + end do + end function cr_get + + subroutine cr_list_all(this) + class(component_registry_type), intent(in) :: this + integer :: i + + if (.not. this%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + if (this%simple_count == 0) then + print *, "[INFO] No simple components registered" + return + end if + + print *, "=== Simple Registry Contents (", this%simple_count, " components) ===" + do i = 1, this%simple_count + call this%simple_components(i)%print() + end do + print *, "==========================================" + end subroutine cr_list_all + + subroutine cr_list_simple(this) + class(component_registry_type), intent(in) :: this + call this%list_all() + end subroutine cr_list_simple + + ! ---------- Factory Registration Methods ---------- + + subroutine cr_register_with_factory(this, entry) + class(component_registry_type), intent(inout) :: this + type(component_registry_entry), intent(in) :: entry + + type(component_registry_entry), allocatable :: temp(:) + integer :: i + + if (.not. this%initialized) then + error stop "[ERROR] Registry not initialized, call initialize_registry first" + end if + + ! Check if already exists + do i = 1, this%factory_count + if (this%factory_components(i)%category == entry%category .and. & + this%factory_components(i)%name == entry%name) then + if (this%verbose) then + print *, "[WARN] Overwriting factory component: ", & + trim(entry%category), ".", trim(entry%name) + end if + this%factory_components(i) = entry + return + end if + end do + + ! Expand array if needed + if (this%factory_count >= this%capacity) then + this%capacity = this%capacity * 2 + allocate(temp(this%capacity)) + temp(1:this%factory_count) = this%factory_components(1:this%factory_count) + call move_alloc(temp, this%factory_components) + + if (this%verbose) then + print *, "[INFO] Factory registry expanded to capacity:", this%capacity + end if + end if + + ! Add component + this%factory_count = this%factory_count + 1 + this%factory_components(this%factory_count) = entry + + if (this%verbose) then + print *, "[FACTORY] Registered with factory: ", & + trim(entry%category), ".", trim(entry%name) + end if + end subroutine cr_register_with_factory + + function cr_get_with_factory(this, category, name) result(entry) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + type(component_registry_entry) :: entry + + integer :: i + + ! Initialize return value as empty + entry%category = "" + entry%name = "" + entry%order = 0 + entry%factory%ptr => null() + entry%has_factory = .false. + + if (.not. this%initialized) then + return + end if + + do i = 1, this%factory_count + if (this%factory_components(i)%category == category .and. & + this%factory_components(i)%name == name) then + entry = this%factory_components(i) + return + end if + end do + end function cr_get_with_factory + + subroutine cr_list_with_factory(this, category) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category + + character(len=32) :: cat_lower + integer :: i, count + + if (.not. this%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + cat_lower = to_lower(trim(adjustl(category))) + + if (this%factory_count == 0) then + print *, "[INFO] No factory components registered" + return + end if + + ! Count components in this category + count = 0 + do i = 1, this%factory_count + if (len_trim(cat_lower) == 0 .or. & + this%factory_components(i)%category == cat_lower) then + count = count + 1 + end if + end do + + if (count == 0) then + if (len_trim(cat_lower) > 0) then + print *, "[INFO] No factory components in category: ", trim(cat_lower) + else + print *, "[INFO] No factory components registered" + end if + return + end if + + print *, "=== Factory Registry Contents (", count, " components) ===" + do i = 1, this%factory_count + if (len_trim(cat_lower) == 0 .or. & + this%factory_components(i)%category == cat_lower) then + call this%factory_components(i)%print_with_factory() + end if + end do + print *, "==========================================" + end subroutine cr_list_with_factory + + subroutine cr_create_from_factory(this, category, name, instance, status) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + class(*), allocatable, intent(out) :: instance + integer, intent(out) :: status + + type(component_registry_entry) :: entry + integer :: i + + status = -1 ! Default: not found + + if (.not. this%initialized) then + if (this%verbose) then + print *, "[ERROR] Registry not initialized" + end if + return + end if + + ! Find the entry + do i = 1, this%factory_count + if (this%factory_components(i)%category == category .and. & + this%factory_components(i)%name == name) then + entry = this%factory_components(i) + + if (entry%has_factory .and. associated(entry%factory%ptr)) then + ! Call the factory function + call entry%factory%ptr(instance) + status = 0 ! Success + + if (this%verbose) then + print *, "[FACTORY] Created component: ", & + trim(category), ".", trim(name) + end if + else + status = -2 ! No factory function + if (this%verbose) then + print *, "[ERROR] No factory function for: ", & + trim(category), ".", trim(name) + end if + end if + return + end if + end do + + ! If we get here, component not found + if (this%verbose) then + print *, "[ERROR] Factory component not found: ", & + trim(category), ".", trim(name) + end if + end subroutine cr_create_from_factory + + ! ---------- Common Methods ---------- + + subroutine cr_clear(this) + class(component_registry_type), intent(inout) :: this + + if (allocated(this%simple_components)) then + deallocate(this%simple_components) + end if + + if (allocated(this%factory_components)) then + deallocate(this%factory_components) + end if + + this%simple_count = 0 + this%factory_count = 0 + this%capacity = 100 + this%initialized = .false. + end subroutine cr_clear + + integer function cr_total_size(this) + class(component_registry_type), intent(in) :: this + cr_total_size = this%simple_count + this%factory_count + end function cr_total_size + + integer function cr_size(this) + class(component_registry_type), intent(in) :: this + cr_size = this%simple_count ! Backward compatibility + end function cr_size + + logical function cr_is_initialized(this) + class(component_registry_type), intent(in) :: this + cr_is_initialized = this%initialized + end function cr_is_initialized + + ! ==================== UTILITY FUNCTIONS ==================== + + function to_lower(str) result(lower_str) + character(len=*), intent(in) :: str + character(len=len(str)) :: lower_str + integer :: i + + do i = 1, len(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + lower_str(i:i) = char(ichar(str(i:i)) + 32) + else + lower_str(i:i) = str(i:i) + end if + end do + end function to_lower + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..46964ce7 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02/src/infrastructure/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "配置基础设施模块...") + +add_library(infrastructure STATIC + config.f90 + mesh.f90 +) + +target_link_libraries(infrastructure PRIVATE core) + +target_include_directories(infrastructure PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS infrastructure + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "基础设施模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/02/src/infrastructure/config.f90 new file mode 100644 index 00000000..d3b1e0df --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02/src/infrastructure/config.f90 @@ -0,0 +1,90 @@ +! src/infrastructure/config.f90 +module config_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, cfd_config, config_print, config_with_reconstruction + + ! CFD configuration type + type :: cfd_config + character(len=20) :: ic_type = "step" + character(len=20) :: recon_scheme = "eno" + character(len=20) :: flux_type = "rusanov" + integer :: rk_order = 1 + real(real64) :: wave_speed = 1.0_real64 + real(real64) :: final_time = 0.625_real64 + real(real64) :: dt = 0.025_real64 + character(len=20) :: boundary_type = "periodic" + real(real64) :: left_boundary_value = 1.0_real64 + real(real64) :: right_boundary_value = 2.0_real64 + integer :: spatial_order = 2 + logical :: verbose = .true. + end type cfd_config + +contains + + subroutine config_print(this) + type(cfd_config), intent(in) :: this + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(this%ic_type) + print *, "Reconstruction: ", trim(this%recon_scheme), " (order:", this%spatial_order, ")" + print *, "Flux type: ", trim(this%flux_type) + print *, "Time integration: RK", this%rk_order + print *, "Wave speed: ", this%wave_speed + print *, "Final time: ", this%final_time + print *, "Time step: ", this%dt + print *, "Boundary: ", trim(this%boundary_type) + if (trim(this%boundary_type) == 'dirichlet') then + print *, " Dirichlet values: [", this%left_boundary_value, ", ", & + this%right_boundary_value, "]" + end if + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(this, scheme, order) + type(cfd_config), intent(inout) :: this + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + character(len=20) :: scheme_lower + + ! Convert to lowercase + scheme_lower = scheme + call to_lower_inplace(scheme_lower) + this%recon_scheme = trim(adjustl(scheme_lower)) + + ! Set order + if (present(order)) then + this%spatial_order = order + else + ! Smart defaults + if (index(this%recon_scheme, 'weno') > 0) then + this%spatial_order = 5 + else if (trim(this%recon_scheme) == 'eno') then + this%spatial_order = 3 + else + print *, "[ERROR] Unsupported reconstruction scheme: ", trim(this%recon_scheme) + return + end if + end if + + if (this%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(this%recon_scheme), & + " Order: ", this%spatial_order + end if + end subroutine config_with_reconstruction + + subroutine to_lower_inplace(str) + character(len=*), intent(inout) :: str + integer :: i + + do i = 1, len_trim(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + str(i:i) = char(ichar(str(i:i)) + 32) + end if + end do + end subroutine to_lower_inplace + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/02/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..896969bd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02/src/infrastructure/mesh.f90 @@ -0,0 +1,74 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, mesh_type, mesh_init, mesh_print_info + + ! mesh + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer :: ncells = 40 + integer :: nnodes + integer :: nx + real(wp), allocatable :: x(:) ! + real(wp), allocatable :: xcc(:) ! + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer, optional, intent(in) :: ncells + + integer :: i + + ! Set + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! computation + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! node + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! cell + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== ===" + print *, ": [", this%xmin, ", ", this%xmax, "]" + print *, ": ", this%ncells + print *, ": ", this%nnodes + print *, " dx: ", this%dx + print *, " L: ", this%L + print *, "==========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..3fde7746 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,28 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux STATIC + base.f90 + rusanov.f90 +) + +#target_link_libraries(flux PRIVATE core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 明确设置不使用 /Qm64 选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + set_target_properties(flux PROPERTIES + Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /Qm64" # 明确设置,避免继承问题 + ) +endif() + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/02/src/numerics/flux/base.f90 new file mode 100644 index 00000000..7080a7ae --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02/src/numerics/flux/base.f90 @@ -0,0 +1,30 @@ +!src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + procedure :: print_basic_info => flux_print_basic ! 添加辅助方法 + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + + subroutine flux_print_basic(this) + class(flux_calculator_base), intent(in) :: this + print *, " Name: ", trim(this%name) + end subroutine flux_print_basic + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/02/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..7140f710 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02/src/numerics/flux/rusanov.f90 @@ -0,0 +1,41 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + + ! 构造函数接口 + interface rusanov_flux + module procedure create_rusanov_flux + end interface + +contains + + ! 构造函数 + type(rusanov_flux) function create_rusanov_flux() result(this) + this%name = "Rusanov" + this%wave_speed_default = 1.0_real64 + end function create_rusanov_flux + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Flux calculator information:" + call this%print_basic_info() + + ! 添加Rusanov特有信息 + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..5e4b938d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,22 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor STATIC + base.f90 + eno.f90 + weno3.f90 +) + +target_link_libraries(reconstructor PRIVATE core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/02/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..53798d02 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02/src/numerics/reconstructor/base.f90 @@ -0,0 +1,33 @@ +!src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + procedure :: print_basic_info => reconstructor_print_basic ! 添加一个辅助方法 + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + + subroutine reconstructor_print_basic(this) + class(reconstructor_base), intent(in) :: this + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_print_basic + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/02/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..a468b82e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + + ! 构造函数接口 + interface eno_reconstructor + module procedure create_eno_reconstructor + end interface + +contains + + ! 构造函数 + type(eno_reconstructor) function create_eno_reconstructor() result(this) + this%name = "ENO" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_eno_reconstructor + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加ENO特有信息 + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/02/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..5e954291 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno3_reconstructor + + type, extends(reconstructor_base) :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + + ! 构造函数接口 + interface weno3_reconstructor + module procedure create_weno3_reconstructor + end interface + +contains + + ! 构造函数 + type(weno3_reconstructor) function create_weno3_reconstructor() result(this) + this%name = "WENO3" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_weno3_reconstructor + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO3特有信息 + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02/tests/CMakeLists.txt new file mode 100644 index 00000000..0cd64b0c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02/tests/CMakeLists.txt @@ -0,0 +1,43 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +add_executable(test_minimal_simple test_minimal_simple.f90) + +message(STATUS "CMAKE_Fortran_MODULE_DIRECTORY=${CMAKE_Fortran_MODULE_DIRECTORY}") + +#target_include_directories( test_minimal_simple +# PRIVATE +# ${CMAKE_Fortran_MODULE_DIRECTORY} +#) + +target_link_libraries( test_minimal_simple + PRIVATE + infrastructure +) + +add_executable(test_simple_link test_simple_link.f90) +target_link_libraries(test_simple_link + PRIVATE + reconstructor + flux +) + + +#add_executable(test_factory_simple test_factory_simple.f90) + +#target_link_libraries( test_factory_simple +# PRIVATE +# core +# infrastructure +# reconstructor +# flux +#) + +add_executable(test_factory_simple test_factory_simple.f90) +target_link_libraries(test_factory_simple + PRIVATE + core + infrastructure + reconstructor + flux +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02/tests/test_factory_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/02/tests/test_factory_simple.f90 new file mode 100644 index 00000000..d4139bb1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02/tests/test_factory_simple.f90 @@ -0,0 +1,86 @@ +!tests/test_factory_simple.f90 +program test_factory_simple + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module, only: initialize_registry, cleanup_registry, & + register_component_simple, has_component + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Basic systems + print *, "1. Testing basic systems..." + print *, "-----------------------------" + call config_print(config) + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Creating reconstructors + print *, "2. Testing reconstructors..." + print *, "------------------------------" + + ! 创建并测试ENO重构器 + print *, "Creating ENO reconstructor..." + eno = eno_reconstructor() ! 使用构造函数 + call eno%info() ! 必须调用info方法 + + print *, "" + print *, "Creating WENO3 reconstructor..." + weno3 = weno3_reconstructor() ! 使用构造函数 + call weno3%info() ! 必须调用info方法 + print *, "" + + ! Test 3: Creating flux calculator + print *, "3. Testing flux calculator..." + print *, "-------------------------------" + + print *, "Creating Rusanov flux calculator..." + rusanov = rusanov_flux() ! 使用构造函数 + call rusanov%info() ! 必须调用info方法 + print *, "" + + ! Test 4: Registry integration + print *, "4. Testing registry..." + print *, "----------------------" + + call initialize_registry(verbose=.true.) + + ! Register components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! Check registration + if (has_component("reconstructor", "eno")) then + print *, "[OK] ENO reconstructor registered successfully" + end if + + if (has_component("reconstructor", "weno3")) then + print *, "[OK] WENO3 reconstructor registered successfully" + end if + + if (has_component("flux", "rusanov")) then + print *, "[OK] Rusanov flux registered successfully" + end if + + print *, "" + call cleanup_registry() + + print *, "=== Factory pattern simple test completed successfully ===" + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02/tests/test_minimal_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/02/tests/test_minimal_simple.f90 new file mode 100644 index 00000000..6213e8f8 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02/tests/test_minimal_simple.f90 @@ -0,0 +1,84 @@ +! tests/test_minimal_simple.f90 +program test_minimal_simple + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Simple Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components (simple version) + print *, "5. Testing registry functionality" + print *, "---------------------------------" + print *, "Registry initialized: ", registry_is_initialized() + print *, "Number of components: ", registry_get_size() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Simple test completed successfully ===" + +end program test_minimal_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02/tests/test_simple_link.f90 b/example/1d-linear-convection/weno3/fortran/registry/02/tests/test_simple_link.f90 new file mode 100644 index 00000000..807d0bc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02/tests/test_simple_link.f90 @@ -0,0 +1,108 @@ +! tests/test_minimal.f90 +program test_minimal + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i ! Declare loop variable here + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_all() + print *, "Registry size: ", component_registry%size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components + print *, "5. Testing available components" + print *, "--------------------------------" + + ! Use a separate block to avoid allocation issues + call test_available_components() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Minimal test completed successfully ===" + +contains + + ! Internal subroutine for testing available components + subroutine test_available_components() + character(len=:), allocatable :: names(:) + integer, allocatable :: orders(:) + integer :: j + + call get_available_components("reconstructor", names, orders) + + if (allocated(names)) then + print *, "Reconstructors:" + do j = 1, size(names) + print *, " - ", trim(names(j)) + if (allocated(orders)) then + print *, " Order: ", orders(j) + end if + end do + else + print *, "No reconstructors found" + end if + end subroutine test_available_components + +end program test_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02a/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02a/CMakeLists.txt new file mode 100644 index 00000000..ef66d584 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02a/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02a/README.md b/example/1d-linear-convection/weno3/fortran/registry/02a/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02a/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02a/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/02a/scripts/build.bat new file mode 100644 index 00000000..6fd6dc03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02a/scripts/build.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Project Builder +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python build script with full Intel environment support... +echo. + +python build.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Build failed + pause + exit /b 1 +) + +echo. +echo [INFO] Build completed successfully! +echo. +echo [INFO] To run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02a/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/02a/scripts/build.py new file mode 100644 index 00000000..3bf6d537 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02a/scripts/build.py @@ -0,0 +1,629 @@ +#!/usr/bin/env python3 +""" +Fortran CFD Project Builder - 完整Python解决方案 +在Python内部处理Intel oneAPI环境配置 +""" + +import os +import sys +import subprocess +import shutil +import argparse +import time +import platform +import tempfile +from pathlib import Path + +class IntelEnvironment: + """Intel oneAPI环境管理器""" + + def __init__(self): + self.setvars_path = None + self.env_vars = {} + + def find_setvars(self): + """查找setvars.bat文件""" + possible_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + r"C:\Program Files (x86)\Intel\oneAPI\compiler\latest\env\vars.bat", + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\setvars.bat"), + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\compiler\latest\env\vars.bat"), + ] + + for path in possible_paths: + if os.path.exists(path): + self.setvars_path = path + return True + + return False + + def setup_environment(self): + """设置Intel环境""" + if not self.find_setvars(): + return False + + try: + # 创建临时的批处理文件来捕获环境变量 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + f.write(f'@echo off\n') + f.write(f'call "{self.setvars_path}" >nul 2>&1\n') + f.write(f'set\n') # 输出所有环境变量 + temp_bat = f.name + + # 运行批处理文件并捕获输出 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + shell=True + ) + + # 解析环境变量 + for line in result.stdout.split('\n'): + line = line.strip() + if '=' in line: + key, value = line.split('=', 1) + self.env_vars[key.strip()] = value.strip() + + # 清理临时文件 + os.unlink(temp_bat) + + # 更新当前进程的环境变量 + os.environ.update(self.env_vars) + + return True + + except Exception as e: + print(f"设置Intel环境失败: {e}") + return False + + def get_compiler_info(self): + """获取编译器信息""" + info = {} + + # 检查ifx编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + env={**os.environ, **self.env_vars} if self.env_vars else os.environ + ) + + if result.returncode == 0: + for line in result.stdout.split('\n'): + if 'Version' in line or '版本' in line: + info['ifx_version'] = line.strip() + break + except: + pass + + # 检查环境变量 + info['ifx_root'] = self.env_vars.get('IFX_ROOT', '') + info['compiler_root'] = self.env_vars.get('ONEAPI_ROOT', '') + + return info + +class BuildSystem: + """构建系统主类""" + + def __init__(self): + self.project_root = Path(__file__).parent.parent + self.build_dir = self.project_root / "build" + self.intel_env = IntelEnvironment() + + # 设置控制台编码 + if sys.platform == "win32": + try: + import ctypes + # 设置控制台输出为UTF-8 + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + except: + pass + + def print_header(self, text): + """打印标题""" + print(f"\n{'='*70}") + print(f" {text}") + print(f"{'='*70}\n") + + def print_step(self, step, total, message): + """打印步骤""" + print(f"[{step}/{total}] {message}...") + + def print_success(self, message): + """打印成功""" + print(f"\033[92m✓ {message}\033[0m") + + def print_error(self, message): + """打印错误""" + print(f"\033[91m✗ {message}\033[0m") + + def print_warning(self, message): + """打印警告""" + print(f"\033[93m! {message}\033[0m") + + def print_info(self, message): + """打印信息""" + print(f"\033[94mℹ {message}\033[0m") + + def check_prerequisites(self): + """检查前提条件""" + self.print_step(1, 6, "检查前提条件") + + # 检查Python版本 + python_version = sys.version.split()[0] + self.print_info(f"Python版本: {python_version}") + + # 检查平台 + self.print_info(f"平台: {platform.system()} {platform.release()}") + self.print_info(f"处理器核心数: {os.cpu_count()}") + + # 检查CMake + try: + result = subprocess.run( + ["cmake", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + version_line = result.stdout.split('\n')[0] + self.print_success(f"CMake: {version_line}") + else: + self.print_error("CMake未找到") + return False + except FileNotFoundError: + self.print_error("CMake未安装") + return False + + return True + + def setup_intel_environment(self, args): + """设置Intel环境""" + self.print_step(2, 6, "配置Intel oneAPI环境") + + if not self.intel_env.find_setvars(): + self.print_warning("未找到Intel oneAPI setvars.bat") + self.print_info("将尝试使用系统环境中的编译器") + + # 检查是否能直接访问编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + self.print_success("Intel编译器在系统PATH中找到") + return True + else: + self.print_warning("Intel编译器未在PATH中找到") + except: + self.print_warning("无法访问Intel编译器") + + return True # 继续,让CMake自己找编译器 + + # 设置环境 + if self.intel_env.setup_environment(): + compiler_info = self.intel_env.get_compiler_info() + + if compiler_info.get('ifx_version'): + self.print_success(f"Intel Fortran编译器: {compiler_info['ifx_version']}") + elif compiler_info.get('ifx_root'): + self.print_success(f"Intel编译器路径: {compiler_info['ifx_root']}") + else: + self.print_success("Intel oneAPI环境配置完成") + + return True + else: + self.print_warning("Intel环境配置失败,将继续使用系统环境") + return True + + def clean_build_directory(self, args): + """清理构建目录""" + if args.clean and self.build_dir.exists(): + self.print_info("清理构建目录...") + try: + shutil.rmtree(self.build_dir) + self.print_success("构建目录已清理") + except Exception as e: + self.print_error(f"清理失败: {e}") + if not args.force: + return False + return True + + def run_command(self, cmd, cwd=None, check=True, env=None): + """运行命令""" + if isinstance(cmd, list): + cmd_str = ' '.join(str(c) for c in cmd if c) + else: + cmd_str = str(cmd) + + print(f" \033[96m$\033[0m {cmd_str}") + + try: + # 合并环境变量 + exec_env = os.environ.copy() + if env: + exec_env.update(env) + if self.intel_env.env_vars: + exec_env.update(self.intel_env.env_vars) + + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=False, + env=exec_env + ) + + # 处理输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or '完成' in line or '生成' in line: + print(f" \033[92m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + if check and result.returncode != 0: + self.print_error(f"命令执行失败,退出码: {result.returncode}") + return False + + return True + + except Exception as e: + self.print_error(f"命令执行异常: {e}") + return False + + def configure_cmake(self, args): + """配置CMake""" + self.print_step(3, 6, "配置CMake项目") + + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ] + + if args.compiler == "ifx": + cmake_cmd.extend(["-T", "fortran=ifx"]) + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + success = self.run_command(cmake_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("CMake配置完成") + else: + self.print_error("CMake配置失败") + + return success + + def build_project(self, args): + """构建项目""" + self.print_step(4, 6, "构建项目") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + success = self.run_command(build_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("项目构建完成") + else: + self.print_error("构建失败") + + return success + + def run_tests_with_environment(self, test_exe): + """运行单个测试,确保有Intel环境""" + try: + # 创建临时的批处理文件来运行测试 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'"{test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'"{test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + return result + + except Exception as e: + print(f"运行测试失败: {e}") + return None + + def run_tests(self, args): + """运行测试""" + self.print_step(5, 6, "运行测试") + + # 查找测试可执行文件 + test_dir = self.build_dir / "bin" / args.build_type + if not test_dir.exists(): + test_dir = self.build_dir / "bin" + if not test_dir.exists(): + test_dir = self.build_dir + + test_files = list(test_dir.glob("test_*.exe")) + + if not test_files: + self.print_warning("未找到测试程序") + return True + + all_passed = True + + for test_exe in sorted(test_files): + test_name = test_exe.stem + self.print_info(f"运行测试: {test_name}") + print(f" {'-'*50}") + + # 运行测试 + result = self.run_tests_with_environment(str(test_exe)) + + if result is None: + self.print_error(f" {test_name} 运行失败") + all_passed = False + continue + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + print(f" {line}") + + if result.returncode == 0: + self.print_success(f" {test_name} 通过") + else: + self.print_error(f" {test_name} 失败 (退出码: {result.returncode})") + all_passed = False + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + print() # 空行 + + return all_passed + + def create_test_runner(self, args): + """创建独立的测试运行器""" + self.print_step(6, 6, "创建测试运行器") + + runner_path = self.build_dir / "run_tests.bat" + + content = f'''@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Test Runner +echo ======================================== +echo. + +REM Setup Intel oneAPI environment +set "SETVARS_PATH={self.intel_env.setvars_path or ''}" +if exist "%SETVARS_PATH%" ( + call "%SETVARS_PATH%" >nul + echo [INFO] Intel environment configured +) else ( + echo [WARNING] Intel environment not found + echo [WARNING] Tests may fail without runtime libraries +) + +echo. + +REM Run all test executables +set "TEST_COUNT=0" +set "PASS_COUNT=0" + +for %%f in ("bin\\{args.build_type}\\test_*.exe") do ( + set /a TEST_COUNT+=1 + echo [TEST %%f] + echo {'-'*50} + + %%f + if errorlevel 1 ( + echo [FAILED] %%f + ) else ( + echo [PASSED] %%f + set /a PASS_COUNT+=1 + ) + echo. +) + +echo ======================================== +echo Tests: %PASS_COUNT%/%TEST_COUNT% passed +if %PASS_COUNT% equ %TEST_COUNT% ( + echo [SUCCESS] All tests passed! +) else ( + echo [FAILURE] Some tests failed +) +echo ======================================== + +pause +''' + + with open(runner_path, 'w', encoding='utf-8') as f: + f.write(content) + + self.print_success(f"测试运行器已创建: {runner_path}") + self.print_info(f"使用方法: cd build && run_tests.bat") + + return runner_path + + def generate_report(self, args, build_time, tests_passed): + """生成构建报告""" + self.print_header("构建完成") + + print(f"项目: {self.project_root.name}") + print(f"构建类型: {args.build_type}") + print(f"编译器: {args.compiler}") + print(f"并行作业: {args.jobs}") + print(f"总耗时: {build_time:.1f}秒") + print(f"测试结果: {'全部通过' if tests_passed else '有失败'}") + + # 显示生成的可执行文件 + bin_dir = self.build_dir / "bin" / args.build_type + if bin_dir.exists(): + print(f"\n生成的可执行文件:") + for exe in sorted(bin_dir.glob("*.exe")): + size_mb = exe.stat().st_size / (1024 * 1024) + print(f" • {exe.name} ({size_mb:.2f} MB)") + + # 显示测试运行器信息 + runner_path = self.build_dir / "run_tests.bat" + if runner_path.exists(): + print(f"\n独立测试运行器:") + print(f" • {runner_path.name}") + print(f" 在Intel oneAPI环境中运行所有测试") + + def run(self): + """运行构建系统""" + parser = argparse.ArgumentParser( + description="Fortran CFD项目构建工具", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认构建 + %(prog)s --clean # 清理后构建 + %(prog)s --build-type Release # Release构建 + %(prog)s --no-tests # 只构建,不运行测试 + %(prog)s -j8 --verbose # 8线程并行构建,详细输出 + """ + ) + + parser.add_argument("--build-type", choices=["Debug", "Release"], + default="Debug", help="构建类型") + parser.add_argument("--compiler", choices=["ifx", "ifort"], + default="ifx", help="Fortran编译器") + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-tests", action="store_true", + help="跳过测试") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + + args = parser.parse_args() + + # 开始构建 + start_time = time.time() + + self.print_header("Fortran CFD 项目构建系统") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 检查前提条件 + if not self.check_prerequisites(): + if not args.force: + return 1 + + # 2. 设置Intel环境 + if not self.setup_intel_environment(args): + if not args.force: + return 1 + + # 3. 清理目录 + if not self.clean_build_directory(args): + if not args.force: + return 1 + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 4. 配置CMake + if not self.configure_cmake(args): + if not args.force: + return 1 + + # 5. 构建项目 + if not self.build_project(args): + if not args.force: + return 1 + + # 6. 运行测试和创建测试运行器 + tests_passed = True + if not args.no_tests: + tests_passed = self.run_tests(args) + + # 创建测试运行器 + self.create_test_runner(args) # 传递 args 参数 + + # 7. 生成报告 + build_time = time.time() - start_time + self.generate_report(args, build_time, tests_passed) + + return 0 if tests_passed else 1 + + except KeyboardInterrupt: + self.print_error("\n构建被用户中断") + return 1 + except Exception as e: + self.print_error(f"构建过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + builder = BuildSystem() + return builder.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02a/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/02a/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02a/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02a/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02a/src/CMakeLists.txt new file mode 100644 index 00000000..ee38952b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02a/src/CMakeLists.txt @@ -0,0 +1,14 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02a/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02a/src/core/CMakeLists.txt new file mode 100644 index 00000000..bcfd024d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02a/src/core/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/core/CMakeLists.txt +message(STATUS "配置核心模块...") + +add_library(core STATIC + registry.f90 + factory_interfaces.f90 +) + +target_include_directories(core PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 安装规则 +install(TARGETS core + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) +install(FILES + ${CMAKE_Fortran_MODULE_DIRECTORY}/registry_module.mod + ${CMAKE_Fortran_MODULE_DIRECTORY}/factory_interfaces.mod + DESTINATION include/fortran_cfd/core +) + +message(STATUS "核心模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02a/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/02a/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..a960167c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02a/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02a/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/02a/src/core/registry.f90 new file mode 100644 index 00000000..d620f5ec --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02a/src/core/registry.f90 @@ -0,0 +1,656 @@ +! src/core/registry.f90 +module registry_module + use, intrinsic :: iso_fortran_env, only: real64 + use factory_interfaces, only: factory_procedure + implicit none + + private + + ! ==================== PUBLIC INTERFACE ==================== + ! 原有接口保持不变 + public :: real64, component_info, component_registry + public :: register_component_simple, initialize_registry, cleanup_registry + public :: has_component, get_available_components + public :: registry_is_initialized, registry_get_size + + ! 新增工厂相关接口 + public :: factory_ptr_type, component_registry_entry + public :: register_component_with_factory + public :: create_component_from_registry + public :: has_factory_component + public :: list_factory_components + + ! ==================== TYPE DEFINITIONS ==================== + + ! 原有类型定义 + type :: component_info + character(len=32) :: category = "" + character(len=32) :: name = "" + integer :: order = 0 + contains + procedure :: print => ci_print + end type component_info + + ! 新增:工厂指针类型 + type :: factory_ptr_type + procedure(factory_procedure), pointer, nopass :: ptr => null() + end type factory_ptr_type + + ! 新增:带工厂的注册条目 + type :: component_registry_entry + character(len=32) :: category = "" + character(len=32) :: name = "" + integer :: order = 0 + type(factory_ptr_type) :: factory + logical :: has_factory = .false. + contains + procedure :: print_with_factory => entry_print + procedure :: can_create => entry_has_factory + end type component_registry_entry + + ! 原有注册表类型(扩展以支持工厂) + type :: component_registry_type + private + ! 简单注册组件(向后兼容) + type(component_info), allocatable :: simple_components(:) + integer :: simple_count = 0 + + ! 带工厂的组件(新增) + type(component_registry_entry), allocatable :: factory_components(:) + integer :: factory_count = 0 + + integer :: capacity = 100 + logical :: verbose = .true. + logical :: initialized = .false. + contains + ! 简单注册方法 + procedure :: register => cr_register ! ← 保持原名 + procedure :: get => cr_get ! ← 保持原名 + procedure :: list_simple => cr_list_simple ! ← 新增别名 + + ! 工厂注册方法 + procedure :: register_with_factory => cr_register_with_factory + procedure :: get_with_factory => cr_get_with_factory + procedure :: list_with_factory => cr_list_with_factory + procedure :: create_from_factory => cr_create_from_factory + + ! 通用方法 + procedure :: clear => cr_clear + procedure :: size => cr_size + procedure :: total_size => cr_total_size + procedure :: is_initialized => cr_is_initialized + end type component_registry_type + + ! Global registry instance + type(component_registry_type), save :: component_registry + +contains + + ! ==================== PUBLIC API ==================== + + ! Initialize registry (保持不变) + subroutine initialize_registry(initial_capacity, verbose) + integer, optional, intent(in) :: initial_capacity + logical, optional, intent(in) :: verbose + + if (component_registry%initialized) then + if (component_registry%verbose) then + print *, "[INFO] Registry already initialized" + end if + return + end if + + if (present(initial_capacity)) then + component_registry%capacity = max(10, initial_capacity) + end if + + if (present(verbose)) then + component_registry%verbose = verbose + end if + + ! Allocate arrays for both types + allocate(component_registry%simple_components(component_registry%capacity)) + allocate(component_registry%factory_components(component_registry%capacity)) + + component_registry%initialized = .true. + component_registry%simple_count = 0 + component_registry%factory_count = 0 + + if (component_registry%verbose) then + print *, "[INIT] Registry initialized, capacity:", component_registry%capacity + print *, " Supports both simple and factory-based registration" + end if + end subroutine initialize_registry + + ! Cleanup registry (保持不变) + subroutine cleanup_registry + call component_registry%clear() + if (component_registry%verbose) then + print *, "[CLEANUP] Registry cleaned up" + end if + end subroutine cleanup_registry + + ! ==================== SIMPLE REGISTRATION (向后兼容) ==================== + + ! Simple registration (no factory) - 保持不变 + subroutine register_component_simple(category, name) + character(len=*), intent(in) :: category, name + + type(component_info) :: info + + info%category = to_lower(trim(adjustl(category))) + info%name = to_lower(trim(adjustl(name))) + info%order = 0 + + call component_registry%register(info) + end subroutine register_component_simple + + ! Check if component exists (简单注册) + function has_component(category, name) result(found) + character(len=*), intent(in) :: category, name + logical :: found + + type(component_info) :: info + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + info = component_registry%get(cat_lower, name_lower) + found = (len_trim(info%category) > 0) + end function has_component + + ! Get available components in a category (简单注册) + subroutine get_available_components(category, names, orders) + character(len=*), intent(in) :: category + character(len=:), allocatable, intent(out), optional :: names(:) + integer, allocatable, intent(out), optional :: orders(:) + + character(len=32) :: cat_lower + integer :: i, count, idx + type(component_info) :: info + + cat_lower = to_lower(trim(adjustl(category))) + + ! Count components in this category + count = 0 + do i = 1, component_registry%simple_count + if (component_registry%simple_components(i)%category == cat_lower) then + count = count + 1 + end if + end do + + ! Allocate arrays if requested + if (present(names)) then + allocate(character(len=32) :: names(count)) + end if + + if (present(orders)) then + allocate(orders(count)) + end if + + ! Fill arrays + idx = 1 + do i = 1, component_registry%simple_count + if (component_registry%simple_components(i)%category == cat_lower) then + info = component_registry%simple_components(i) + if (present(names)) then + names(idx) = info%name + end if + if (present(orders)) then + orders(idx) = info%order + end if + idx = idx + 1 + end if + end do + end subroutine get_available_components + + ! ==================== FACTORY REGISTRATION (新增) ==================== + + ! Register component with factory function + subroutine register_component_with_factory(category, name, factory_func, order) + character(len=*), intent(in) :: category, name + procedure(factory_procedure) :: factory_func + integer, optional, intent(in) :: order + + type(component_registry_entry) :: entry + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + entry%category = cat_lower + entry%name = name_lower + + if (present(order)) then + entry%order = order + else + entry%order = 0 + end if + + entry%factory%ptr => factory_func + entry%has_factory = .true. + + call component_registry%register_with_factory(entry) + end subroutine register_component_with_factory + + ! Create component from registry using factory + function create_component_from_registry(category, name) result(instance) + character(len=*), intent(in) :: category, name + class(*), allocatable :: instance + + character(len=32) :: cat_lower, name_lower + integer :: status + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + call component_registry%create_from_factory(cat_lower, name_lower, instance, status) + + if (status /= 0) then + if (component_registry%verbose) then + print *, "[ERROR] Failed to create component: ", trim(category), ".", trim(name) + end if + end if + end function create_component_from_registry + + ! Check if component has factory + function has_factory_component(category, name) result(found) + character(len=*), intent(in) :: category, name + logical :: found + + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + found = .false. + + if (.not. component_registry%initialized) return + + ! 简化的查找逻辑(实际应调用内部方法) + found = component_registry%factory_count > 0 ! 占位实现 + end function has_factory_component + + ! List all factory components + subroutine list_factory_components(category) + character(len=*), optional, intent(in) :: category + + if (present(category)) then + call component_registry%list_with_factory(category) + else + call component_registry%list_with_factory("") + end if + end subroutine list_factory_components + + ! ==================== PUBLIC UTILITY FUNCTIONS ==================== + + ! Public function to check if registry is initialized + function registry_is_initialized() result(is_initialized) + logical :: is_initialized + is_initialized = component_registry%is_initialized() + end function registry_is_initialized + + ! Public function to get total registry size + function registry_get_size() result(size_val) + integer :: size_val + size_val = component_registry%total_size() + end function registry_get_size + + ! ==================== COMPONENT INFO METHODS ==================== + + subroutine ci_print(this) + class(component_info), intent(in) :: this + + if (this%order > 0) then + print *, " [", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")]" + else + print *, " [", trim(this%category), ".", trim(this%name), "]" + end if + end subroutine ci_print + + subroutine entry_print(this) + class(component_registry_entry), intent(in) :: this + + if (this%has_factory) then + if (this%order > 0) then + print *, " [FACTORY] ", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")" + else + print *, " [FACTORY] ", trim(this%category), ".", trim(this%name) + end if + else + if (this%order > 0) then + print *, " [SIMPLE] ", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")" + else + print *, " [SIMPLE] ", trim(this%category), ".", trim(this%name) + end if + end if + end subroutine entry_print + + logical function entry_has_factory(this) + class(component_registry_entry), intent(in) :: this + entry_has_factory = this%has_factory + end function entry_has_factory + + ! ==================== REGISTRY INTERNAL METHODS ==================== + + ! ---------- Simple Registration Methods ---------- + + subroutine cr_register(this, info) + class(component_registry_type), intent(inout) :: this + type(component_info), intent(in) :: info + + type(component_info), allocatable :: temp(:) + integer :: i + + if (.not. this%initialized) then + error stop "[ERROR] Registry not initialized, call initialize_registry first" + end if + + ! Check if already exists + do i = 1, this%simple_count + if (this%simple_components(i)%category == info%category .and. & + this%simple_components(i)%name == info%name) then + if (this%verbose) then + print *, "[WARN] Overwriting simple component: ", & + trim(info%category), ".", trim(info%name) + end if + this%simple_components(i) = info + return + end if + end do + + ! Expand array if needed + if (this%simple_count >= this%capacity) then + this%capacity = this%capacity * 2 + allocate(temp(this%capacity)) + temp(1:this%simple_count) = this%simple_components(1:this%simple_count) + call move_alloc(temp, this%simple_components) + + if (this%verbose) then + print *, "[INFO] Simple registry expanded to capacity:", this%capacity + end if + end if + + ! Add component + this%simple_count = this%simple_count + 1 + this%simple_components(this%simple_count) = info + + if (this%verbose) then + print *, "[OK] Registered simple: ", trim(info%category), ".", trim(info%name) + end if + end subroutine cr_register + + function cr_get(this, category, name) result(info) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + type(component_info) :: info + + integer :: i + + ! Initialize return value as empty + info%category = "" + info%name = "" + info%order = 0 + + if (.not. this%initialized) then + return + end if + + do i = 1, this%simple_count + if (this%simple_components(i)%category == category .and. & + this%simple_components(i)%name == name) then + info = this%simple_components(i) + return + end if + end do + end function cr_get + + subroutine cr_list_simple(this) + class(component_registry_type), intent(in) :: this + integer :: i + + if (.not. this%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + if (this%simple_count == 0) then + print *, "[INFO] No simple components registered" + return + end if + + print *, "=== Simple Registry Contents (", this%simple_count, " components) ===" + do i = 1, this%simple_count + call this%simple_components(i)%print() + end do + print *, "==========================================" + end subroutine cr_list_simple + + ! ---------- Factory Registration Methods ---------- + + subroutine cr_register_with_factory(this, entry) + class(component_registry_type), intent(inout) :: this + type(component_registry_entry), intent(in) :: entry + + type(component_registry_entry), allocatable :: temp(:) + integer :: i + + if (.not. this%initialized) then + error stop "[ERROR] Registry not initialized, call initialize_registry first" + end if + + ! Check if already exists + do i = 1, this%factory_count + if (this%factory_components(i)%category == entry%category .and. & + this%factory_components(i)%name == entry%name) then + if (this%verbose) then + print *, "[WARN] Overwriting factory component: ", & + trim(entry%category), ".", trim(entry%name) + end if + this%factory_components(i) = entry + return + end if + end do + + ! Expand array if needed + if (this%factory_count >= this%capacity) then + this%capacity = this%capacity * 2 + allocate(temp(this%capacity)) + temp(1:this%factory_count) = this%factory_components(1:this%factory_count) + call move_alloc(temp, this%factory_components) + + if (this%verbose) then + print *, "[INFO] Factory registry expanded to capacity:", this%capacity + end if + end if + + ! Add component + this%factory_count = this%factory_count + 1 + this%factory_components(this%factory_count) = entry + + if (this%verbose) then + print *, "[FACTORY] Registered with factory: ", & + trim(entry%category), ".", trim(entry%name) + end if + end subroutine cr_register_with_factory + + function cr_get_with_factory(this, category, name) result(entry) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + type(component_registry_entry) :: entry + + integer :: i + + ! Initialize return value as empty + entry%category = "" + entry%name = "" + entry%order = 0 + entry%factory%ptr => null() + entry%has_factory = .false. + + if (.not. this%initialized) then + return + end if + + do i = 1, this%factory_count + if (this%factory_components(i)%category == category .and. & + this%factory_components(i)%name == name) then + entry = this%factory_components(i) + return + end if + end do + end function cr_get_with_factory + + subroutine cr_list_with_factory(this, category) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category + + character(len=32) :: cat_lower + integer :: i, count + + if (.not. this%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + cat_lower = to_lower(trim(adjustl(category))) + + if (this%factory_count == 0) then + print *, "[INFO] No factory components registered" + return + end if + + ! Count components in this category + count = 0 + do i = 1, this%factory_count + if (len_trim(cat_lower) == 0 .or. & + this%factory_components(i)%category == cat_lower) then + count = count + 1 + end if + end do + + if (count == 0) then + if (len_trim(cat_lower) > 0) then + print *, "[INFO] No factory components in category: ", trim(cat_lower) + else + print *, "[INFO] No factory components registered" + end if + return + end if + + print *, "=== Factory Registry Contents (", count, " components) ===" + do i = 1, this%factory_count + if (len_trim(cat_lower) == 0 .or. & + this%factory_components(i)%category == cat_lower) then + call this%factory_components(i)%print_with_factory() + end if + end do + print *, "==========================================" + end subroutine cr_list_with_factory + + subroutine cr_create_from_factory(this, category, name, instance, status) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + class(*), allocatable, intent(out) :: instance + integer, intent(out) :: status + + type(component_registry_entry) :: entry + integer :: i + + status = -1 ! Default: not found + + if (.not. this%initialized) then + if (this%verbose) then + print *, "[ERROR] Registry not initialized" + end if + return + end if + + ! Find the entry + do i = 1, this%factory_count + if (this%factory_components(i)%category == category .and. & + this%factory_components(i)%name == name) then + entry = this%factory_components(i) + + if (entry%has_factory .and. associated(entry%factory%ptr)) then + ! Call the factory function + call entry%factory%ptr(instance) + status = 0 ! Success + + if (this%verbose) then + print *, "[FACTORY] Created component: ", & + trim(category), ".", trim(name) + end if + else + status = -2 ! No factory function + if (this%verbose) then + print *, "[ERROR] No factory function for: ", & + trim(category), ".", trim(name) + end if + end if + return + end if + end do + + ! If we get here, component not found + if (this%verbose) then + print *, "[ERROR] Factory component not found: ", & + trim(category), ".", trim(name) + end if + end subroutine cr_create_from_factory + + ! ---------- Common Methods ---------- + + subroutine cr_clear(this) + class(component_registry_type), intent(inout) :: this + + if (allocated(this%simple_components)) then + deallocate(this%simple_components) + end if + + if (allocated(this%factory_components)) then + deallocate(this%factory_components) + end if + + this%simple_count = 0 + this%factory_count = 0 + this%capacity = 100 + this%initialized = .false. + end subroutine cr_clear + + integer function cr_total_size(this) + class(component_registry_type), intent(in) :: this + cr_total_size = this%simple_count + this%factory_count + end function cr_total_size + + integer function cr_size(this) + class(component_registry_type), intent(in) :: this + cr_size = this%simple_count ! Backward compatibility + end function cr_size + + logical function cr_is_initialized(this) + class(component_registry_type), intent(in) :: this + cr_is_initialized = this%initialized + end function cr_is_initialized + + ! ==================== UTILITY FUNCTIONS ==================== + + function to_lower(str) result(lower_str) + character(len=*), intent(in) :: str + character(len=len(str)) :: lower_str + integer :: i + + do i = 1, len(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + lower_str(i:i) = char(ichar(str(i:i)) + 32) + else + lower_str(i:i) = str(i:i) + end if + end do + end function to_lower + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02a/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02a/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..46964ce7 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02a/src/infrastructure/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "配置基础设施模块...") + +add_library(infrastructure STATIC + config.f90 + mesh.f90 +) + +target_link_libraries(infrastructure PRIVATE core) + +target_include_directories(infrastructure PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS infrastructure + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "基础设施模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02a/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/02a/src/infrastructure/config.f90 new file mode 100644 index 00000000..d3b1e0df --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02a/src/infrastructure/config.f90 @@ -0,0 +1,90 @@ +! src/infrastructure/config.f90 +module config_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, cfd_config, config_print, config_with_reconstruction + + ! CFD configuration type + type :: cfd_config + character(len=20) :: ic_type = "step" + character(len=20) :: recon_scheme = "eno" + character(len=20) :: flux_type = "rusanov" + integer :: rk_order = 1 + real(real64) :: wave_speed = 1.0_real64 + real(real64) :: final_time = 0.625_real64 + real(real64) :: dt = 0.025_real64 + character(len=20) :: boundary_type = "periodic" + real(real64) :: left_boundary_value = 1.0_real64 + real(real64) :: right_boundary_value = 2.0_real64 + integer :: spatial_order = 2 + logical :: verbose = .true. + end type cfd_config + +contains + + subroutine config_print(this) + type(cfd_config), intent(in) :: this + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(this%ic_type) + print *, "Reconstruction: ", trim(this%recon_scheme), " (order:", this%spatial_order, ")" + print *, "Flux type: ", trim(this%flux_type) + print *, "Time integration: RK", this%rk_order + print *, "Wave speed: ", this%wave_speed + print *, "Final time: ", this%final_time + print *, "Time step: ", this%dt + print *, "Boundary: ", trim(this%boundary_type) + if (trim(this%boundary_type) == 'dirichlet') then + print *, " Dirichlet values: [", this%left_boundary_value, ", ", & + this%right_boundary_value, "]" + end if + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(this, scheme, order) + type(cfd_config), intent(inout) :: this + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + character(len=20) :: scheme_lower + + ! Convert to lowercase + scheme_lower = scheme + call to_lower_inplace(scheme_lower) + this%recon_scheme = trim(adjustl(scheme_lower)) + + ! Set order + if (present(order)) then + this%spatial_order = order + else + ! Smart defaults + if (index(this%recon_scheme, 'weno') > 0) then + this%spatial_order = 5 + else if (trim(this%recon_scheme) == 'eno') then + this%spatial_order = 3 + else + print *, "[ERROR] Unsupported reconstruction scheme: ", trim(this%recon_scheme) + return + end if + end if + + if (this%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(this%recon_scheme), & + " Order: ", this%spatial_order + end if + end subroutine config_with_reconstruction + + subroutine to_lower_inplace(str) + character(len=*), intent(inout) :: str + integer :: i + + do i = 1, len_trim(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + str(i:i) = char(ichar(str(i:i)) + 32) + end if + end do + end subroutine to_lower_inplace + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02a/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/02a/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..896969bd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02a/src/infrastructure/mesh.f90 @@ -0,0 +1,74 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, mesh_type, mesh_init, mesh_print_info + + ! mesh + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer :: ncells = 40 + integer :: nnodes + integer :: nx + real(wp), allocatable :: x(:) ! + real(wp), allocatable :: xcc(:) ! + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer, optional, intent(in) :: ncells + + integer :: i + + ! Set + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! computation + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! node + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! cell + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== ===" + print *, ": [", this%xmin, ", ", this%xmax, "]" + print *, ": ", this%ncells + print *, ": ", this%nnodes + print *, " dx: ", this%dx + print *, " L: ", this%L + print *, "==========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02a/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02a/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02a/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02a/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02a/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..3fde7746 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02a/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,28 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux STATIC + base.f90 + rusanov.f90 +) + +#target_link_libraries(flux PRIVATE core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 明确设置不使用 /Qm64 选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + set_target_properties(flux PROPERTIES + Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /Qm64" # 明确设置,避免继承问题 + ) +endif() + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02a/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/02a/src/numerics/flux/base.f90 new file mode 100644 index 00000000..7080a7ae --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02a/src/numerics/flux/base.f90 @@ -0,0 +1,30 @@ +!src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + procedure :: print_basic_info => flux_print_basic ! 添加辅助方法 + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + + subroutine flux_print_basic(this) + class(flux_calculator_base), intent(in) :: this + print *, " Name: ", trim(this%name) + end subroutine flux_print_basic + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02a/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/02a/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..7140f710 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02a/src/numerics/flux/rusanov.f90 @@ -0,0 +1,41 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + + ! 构造函数接口 + interface rusanov_flux + module procedure create_rusanov_flux + end interface + +contains + + ! 构造函数 + type(rusanov_flux) function create_rusanov_flux() result(this) + this%name = "Rusanov" + this%wave_speed_default = 1.0_real64 + end function create_rusanov_flux + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Flux calculator information:" + call this%print_basic_info() + + ! 添加Rusanov特有信息 + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02a/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02a/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..5e4b938d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02a/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,22 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor STATIC + base.f90 + eno.f90 + weno3.f90 +) + +target_link_libraries(reconstructor PRIVATE core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02a/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/02a/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..53798d02 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02a/src/numerics/reconstructor/base.f90 @@ -0,0 +1,33 @@ +!src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + procedure :: print_basic_info => reconstructor_print_basic ! 添加一个辅助方法 + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + + subroutine reconstructor_print_basic(this) + class(reconstructor_base), intent(in) :: this + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_print_basic + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02a/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/02a/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..a468b82e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02a/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + + ! 构造函数接口 + interface eno_reconstructor + module procedure create_eno_reconstructor + end interface + +contains + + ! 构造函数 + type(eno_reconstructor) function create_eno_reconstructor() result(this) + this%name = "ENO" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_eno_reconstructor + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加ENO特有信息 + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02a/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/02a/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..5e954291 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02a/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno3_reconstructor + + type, extends(reconstructor_base) :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + + ! 构造函数接口 + interface weno3_reconstructor + module procedure create_weno3_reconstructor + end interface + +contains + + ! 构造函数 + type(weno3_reconstructor) function create_weno3_reconstructor() result(this) + this%name = "WENO3" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_weno3_reconstructor + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO3特有信息 + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02a/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02a/tests/CMakeLists.txt new file mode 100644 index 00000000..0cd64b0c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02a/tests/CMakeLists.txt @@ -0,0 +1,43 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +add_executable(test_minimal_simple test_minimal_simple.f90) + +message(STATUS "CMAKE_Fortran_MODULE_DIRECTORY=${CMAKE_Fortran_MODULE_DIRECTORY}") + +#target_include_directories( test_minimal_simple +# PRIVATE +# ${CMAKE_Fortran_MODULE_DIRECTORY} +#) + +target_link_libraries( test_minimal_simple + PRIVATE + infrastructure +) + +add_executable(test_simple_link test_simple_link.f90) +target_link_libraries(test_simple_link + PRIVATE + reconstructor + flux +) + + +#add_executable(test_factory_simple test_factory_simple.f90) + +#target_link_libraries( test_factory_simple +# PRIVATE +# core +# infrastructure +# reconstructor +# flux +#) + +add_executable(test_factory_simple test_factory_simple.f90) +target_link_libraries(test_factory_simple + PRIVATE + core + infrastructure + reconstructor + flux +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02a/tests/test_factory_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/02a/tests/test_factory_simple.f90 new file mode 100644 index 00000000..d4139bb1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02a/tests/test_factory_simple.f90 @@ -0,0 +1,86 @@ +!tests/test_factory_simple.f90 +program test_factory_simple + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module, only: initialize_registry, cleanup_registry, & + register_component_simple, has_component + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Basic systems + print *, "1. Testing basic systems..." + print *, "-----------------------------" + call config_print(config) + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Creating reconstructors + print *, "2. Testing reconstructors..." + print *, "------------------------------" + + ! 创建并测试ENO重构器 + print *, "Creating ENO reconstructor..." + eno = eno_reconstructor() ! 使用构造函数 + call eno%info() ! 必须调用info方法 + + print *, "" + print *, "Creating WENO3 reconstructor..." + weno3 = weno3_reconstructor() ! 使用构造函数 + call weno3%info() ! 必须调用info方法 + print *, "" + + ! Test 3: Creating flux calculator + print *, "3. Testing flux calculator..." + print *, "-------------------------------" + + print *, "Creating Rusanov flux calculator..." + rusanov = rusanov_flux() ! 使用构造函数 + call rusanov%info() ! 必须调用info方法 + print *, "" + + ! Test 4: Registry integration + print *, "4. Testing registry..." + print *, "----------------------" + + call initialize_registry(verbose=.true.) + + ! Register components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! Check registration + if (has_component("reconstructor", "eno")) then + print *, "[OK] ENO reconstructor registered successfully" + end if + + if (has_component("reconstructor", "weno3")) then + print *, "[OK] WENO3 reconstructor registered successfully" + end if + + if (has_component("flux", "rusanov")) then + print *, "[OK] Rusanov flux registered successfully" + end if + + print *, "" + call cleanup_registry() + + print *, "=== Factory pattern simple test completed successfully ===" + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02a/tests/test_minimal_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/02a/tests/test_minimal_simple.f90 new file mode 100644 index 00000000..cc6753b5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02a/tests/test_minimal_simple.f90 @@ -0,0 +1,84 @@ +! tests/test_minimal_simple.f90 +program test_minimal_simple + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Simple Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_simple() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components (simple version) + print *, "5. Testing registry functionality" + print *, "---------------------------------" + print *, "Registry initialized: ", registry_is_initialized() + print *, "Number of components: ", registry_get_size() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Simple test completed successfully ===" + +end program test_minimal_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02a/tests/test_simple_link.f90 b/example/1d-linear-convection/weno3/fortran/registry/02a/tests/test_simple_link.f90 new file mode 100644 index 00000000..27b165b0 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02a/tests/test_simple_link.f90 @@ -0,0 +1,108 @@ +! tests/test_minimal.f90 +program test_minimal + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i ! Declare loop variable here + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_simple() + print *, "Registry size: ", component_registry%size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components + print *, "5. Testing available components" + print *, "--------------------------------" + + ! Use a separate block to avoid allocation issues + call test_available_components() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Minimal test completed successfully ===" + +contains + + ! Internal subroutine for testing available components + subroutine test_available_components() + character(len=:), allocatable :: names(:) + integer, allocatable :: orders(:) + integer :: j + + call get_available_components("reconstructor", names, orders) + + if (allocated(names)) then + print *, "Reconstructors:" + do j = 1, size(names) + print *, " - ", trim(names(j)) + if (allocated(orders)) then + print *, " Order: ", orders(j) + end if + end do + else + print *, "No reconstructors found" + end if + end subroutine test_available_components + +end program test_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02b/CMakeLists.txt new file mode 100644 index 00000000..ef66d584 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/README.md b/example/1d-linear-convection/weno3/fortran/registry/02b/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/02b/scripts/build.bat new file mode 100644 index 00000000..6fd6dc03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/scripts/build.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Project Builder +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python build script with full Intel environment support... +echo. + +python build.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Build failed + pause + exit /b 1 +) + +echo. +echo [INFO] Build completed successfully! +echo. +echo [INFO] To run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/02b/scripts/build.py new file mode 100644 index 00000000..3bf6d537 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/scripts/build.py @@ -0,0 +1,629 @@ +#!/usr/bin/env python3 +""" +Fortran CFD Project Builder - 完整Python解决方案 +在Python内部处理Intel oneAPI环境配置 +""" + +import os +import sys +import subprocess +import shutil +import argparse +import time +import platform +import tempfile +from pathlib import Path + +class IntelEnvironment: + """Intel oneAPI环境管理器""" + + def __init__(self): + self.setvars_path = None + self.env_vars = {} + + def find_setvars(self): + """查找setvars.bat文件""" + possible_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + r"C:\Program Files (x86)\Intel\oneAPI\compiler\latest\env\vars.bat", + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\setvars.bat"), + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\compiler\latest\env\vars.bat"), + ] + + for path in possible_paths: + if os.path.exists(path): + self.setvars_path = path + return True + + return False + + def setup_environment(self): + """设置Intel环境""" + if not self.find_setvars(): + return False + + try: + # 创建临时的批处理文件来捕获环境变量 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + f.write(f'@echo off\n') + f.write(f'call "{self.setvars_path}" >nul 2>&1\n') + f.write(f'set\n') # 输出所有环境变量 + temp_bat = f.name + + # 运行批处理文件并捕获输出 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + shell=True + ) + + # 解析环境变量 + for line in result.stdout.split('\n'): + line = line.strip() + if '=' in line: + key, value = line.split('=', 1) + self.env_vars[key.strip()] = value.strip() + + # 清理临时文件 + os.unlink(temp_bat) + + # 更新当前进程的环境变量 + os.environ.update(self.env_vars) + + return True + + except Exception as e: + print(f"设置Intel环境失败: {e}") + return False + + def get_compiler_info(self): + """获取编译器信息""" + info = {} + + # 检查ifx编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + env={**os.environ, **self.env_vars} if self.env_vars else os.environ + ) + + if result.returncode == 0: + for line in result.stdout.split('\n'): + if 'Version' in line or '版本' in line: + info['ifx_version'] = line.strip() + break + except: + pass + + # 检查环境变量 + info['ifx_root'] = self.env_vars.get('IFX_ROOT', '') + info['compiler_root'] = self.env_vars.get('ONEAPI_ROOT', '') + + return info + +class BuildSystem: + """构建系统主类""" + + def __init__(self): + self.project_root = Path(__file__).parent.parent + self.build_dir = self.project_root / "build" + self.intel_env = IntelEnvironment() + + # 设置控制台编码 + if sys.platform == "win32": + try: + import ctypes + # 设置控制台输出为UTF-8 + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + except: + pass + + def print_header(self, text): + """打印标题""" + print(f"\n{'='*70}") + print(f" {text}") + print(f"{'='*70}\n") + + def print_step(self, step, total, message): + """打印步骤""" + print(f"[{step}/{total}] {message}...") + + def print_success(self, message): + """打印成功""" + print(f"\033[92m✓ {message}\033[0m") + + def print_error(self, message): + """打印错误""" + print(f"\033[91m✗ {message}\033[0m") + + def print_warning(self, message): + """打印警告""" + print(f"\033[93m! {message}\033[0m") + + def print_info(self, message): + """打印信息""" + print(f"\033[94mℹ {message}\033[0m") + + def check_prerequisites(self): + """检查前提条件""" + self.print_step(1, 6, "检查前提条件") + + # 检查Python版本 + python_version = sys.version.split()[0] + self.print_info(f"Python版本: {python_version}") + + # 检查平台 + self.print_info(f"平台: {platform.system()} {platform.release()}") + self.print_info(f"处理器核心数: {os.cpu_count()}") + + # 检查CMake + try: + result = subprocess.run( + ["cmake", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + version_line = result.stdout.split('\n')[0] + self.print_success(f"CMake: {version_line}") + else: + self.print_error("CMake未找到") + return False + except FileNotFoundError: + self.print_error("CMake未安装") + return False + + return True + + def setup_intel_environment(self, args): + """设置Intel环境""" + self.print_step(2, 6, "配置Intel oneAPI环境") + + if not self.intel_env.find_setvars(): + self.print_warning("未找到Intel oneAPI setvars.bat") + self.print_info("将尝试使用系统环境中的编译器") + + # 检查是否能直接访问编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + self.print_success("Intel编译器在系统PATH中找到") + return True + else: + self.print_warning("Intel编译器未在PATH中找到") + except: + self.print_warning("无法访问Intel编译器") + + return True # 继续,让CMake自己找编译器 + + # 设置环境 + if self.intel_env.setup_environment(): + compiler_info = self.intel_env.get_compiler_info() + + if compiler_info.get('ifx_version'): + self.print_success(f"Intel Fortran编译器: {compiler_info['ifx_version']}") + elif compiler_info.get('ifx_root'): + self.print_success(f"Intel编译器路径: {compiler_info['ifx_root']}") + else: + self.print_success("Intel oneAPI环境配置完成") + + return True + else: + self.print_warning("Intel环境配置失败,将继续使用系统环境") + return True + + def clean_build_directory(self, args): + """清理构建目录""" + if args.clean and self.build_dir.exists(): + self.print_info("清理构建目录...") + try: + shutil.rmtree(self.build_dir) + self.print_success("构建目录已清理") + except Exception as e: + self.print_error(f"清理失败: {e}") + if not args.force: + return False + return True + + def run_command(self, cmd, cwd=None, check=True, env=None): + """运行命令""" + if isinstance(cmd, list): + cmd_str = ' '.join(str(c) for c in cmd if c) + else: + cmd_str = str(cmd) + + print(f" \033[96m$\033[0m {cmd_str}") + + try: + # 合并环境变量 + exec_env = os.environ.copy() + if env: + exec_env.update(env) + if self.intel_env.env_vars: + exec_env.update(self.intel_env.env_vars) + + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=False, + env=exec_env + ) + + # 处理输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or '完成' in line or '生成' in line: + print(f" \033[92m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + if check and result.returncode != 0: + self.print_error(f"命令执行失败,退出码: {result.returncode}") + return False + + return True + + except Exception as e: + self.print_error(f"命令执行异常: {e}") + return False + + def configure_cmake(self, args): + """配置CMake""" + self.print_step(3, 6, "配置CMake项目") + + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ] + + if args.compiler == "ifx": + cmake_cmd.extend(["-T", "fortran=ifx"]) + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + success = self.run_command(cmake_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("CMake配置完成") + else: + self.print_error("CMake配置失败") + + return success + + def build_project(self, args): + """构建项目""" + self.print_step(4, 6, "构建项目") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + success = self.run_command(build_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("项目构建完成") + else: + self.print_error("构建失败") + + return success + + def run_tests_with_environment(self, test_exe): + """运行单个测试,确保有Intel环境""" + try: + # 创建临时的批处理文件来运行测试 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'"{test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'"{test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + return result + + except Exception as e: + print(f"运行测试失败: {e}") + return None + + def run_tests(self, args): + """运行测试""" + self.print_step(5, 6, "运行测试") + + # 查找测试可执行文件 + test_dir = self.build_dir / "bin" / args.build_type + if not test_dir.exists(): + test_dir = self.build_dir / "bin" + if not test_dir.exists(): + test_dir = self.build_dir + + test_files = list(test_dir.glob("test_*.exe")) + + if not test_files: + self.print_warning("未找到测试程序") + return True + + all_passed = True + + for test_exe in sorted(test_files): + test_name = test_exe.stem + self.print_info(f"运行测试: {test_name}") + print(f" {'-'*50}") + + # 运行测试 + result = self.run_tests_with_environment(str(test_exe)) + + if result is None: + self.print_error(f" {test_name} 运行失败") + all_passed = False + continue + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + print(f" {line}") + + if result.returncode == 0: + self.print_success(f" {test_name} 通过") + else: + self.print_error(f" {test_name} 失败 (退出码: {result.returncode})") + all_passed = False + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + print() # 空行 + + return all_passed + + def create_test_runner(self, args): + """创建独立的测试运行器""" + self.print_step(6, 6, "创建测试运行器") + + runner_path = self.build_dir / "run_tests.bat" + + content = f'''@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Test Runner +echo ======================================== +echo. + +REM Setup Intel oneAPI environment +set "SETVARS_PATH={self.intel_env.setvars_path or ''}" +if exist "%SETVARS_PATH%" ( + call "%SETVARS_PATH%" >nul + echo [INFO] Intel environment configured +) else ( + echo [WARNING] Intel environment not found + echo [WARNING] Tests may fail without runtime libraries +) + +echo. + +REM Run all test executables +set "TEST_COUNT=0" +set "PASS_COUNT=0" + +for %%f in ("bin\\{args.build_type}\\test_*.exe") do ( + set /a TEST_COUNT+=1 + echo [TEST %%f] + echo {'-'*50} + + %%f + if errorlevel 1 ( + echo [FAILED] %%f + ) else ( + echo [PASSED] %%f + set /a PASS_COUNT+=1 + ) + echo. +) + +echo ======================================== +echo Tests: %PASS_COUNT%/%TEST_COUNT% passed +if %PASS_COUNT% equ %TEST_COUNT% ( + echo [SUCCESS] All tests passed! +) else ( + echo [FAILURE] Some tests failed +) +echo ======================================== + +pause +''' + + with open(runner_path, 'w', encoding='utf-8') as f: + f.write(content) + + self.print_success(f"测试运行器已创建: {runner_path}") + self.print_info(f"使用方法: cd build && run_tests.bat") + + return runner_path + + def generate_report(self, args, build_time, tests_passed): + """生成构建报告""" + self.print_header("构建完成") + + print(f"项目: {self.project_root.name}") + print(f"构建类型: {args.build_type}") + print(f"编译器: {args.compiler}") + print(f"并行作业: {args.jobs}") + print(f"总耗时: {build_time:.1f}秒") + print(f"测试结果: {'全部通过' if tests_passed else '有失败'}") + + # 显示生成的可执行文件 + bin_dir = self.build_dir / "bin" / args.build_type + if bin_dir.exists(): + print(f"\n生成的可执行文件:") + for exe in sorted(bin_dir.glob("*.exe")): + size_mb = exe.stat().st_size / (1024 * 1024) + print(f" • {exe.name} ({size_mb:.2f} MB)") + + # 显示测试运行器信息 + runner_path = self.build_dir / "run_tests.bat" + if runner_path.exists(): + print(f"\n独立测试运行器:") + print(f" • {runner_path.name}") + print(f" 在Intel oneAPI环境中运行所有测试") + + def run(self): + """运行构建系统""" + parser = argparse.ArgumentParser( + description="Fortran CFD项目构建工具", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认构建 + %(prog)s --clean # 清理后构建 + %(prog)s --build-type Release # Release构建 + %(prog)s --no-tests # 只构建,不运行测试 + %(prog)s -j8 --verbose # 8线程并行构建,详细输出 + """ + ) + + parser.add_argument("--build-type", choices=["Debug", "Release"], + default="Debug", help="构建类型") + parser.add_argument("--compiler", choices=["ifx", "ifort"], + default="ifx", help="Fortran编译器") + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-tests", action="store_true", + help="跳过测试") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + + args = parser.parse_args() + + # 开始构建 + start_time = time.time() + + self.print_header("Fortran CFD 项目构建系统") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 检查前提条件 + if not self.check_prerequisites(): + if not args.force: + return 1 + + # 2. 设置Intel环境 + if not self.setup_intel_environment(args): + if not args.force: + return 1 + + # 3. 清理目录 + if not self.clean_build_directory(args): + if not args.force: + return 1 + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 4. 配置CMake + if not self.configure_cmake(args): + if not args.force: + return 1 + + # 5. 构建项目 + if not self.build_project(args): + if not args.force: + return 1 + + # 6. 运行测试和创建测试运行器 + tests_passed = True + if not args.no_tests: + tests_passed = self.run_tests(args) + + # 创建测试运行器 + self.create_test_runner(args) # 传递 args 参数 + + # 7. 生成报告 + build_time = time.time() - start_time + self.generate_report(args, build_time, tests_passed) + + return 0 if tests_passed else 1 + + except KeyboardInterrupt: + self.print_error("\n构建被用户中断") + return 1 + except Exception as e: + self.print_error(f"构建过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + builder = BuildSystem() + return builder.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/02b/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02b/src/CMakeLists.txt new file mode 100644 index 00000000..f958422d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/src/CMakeLists.txt @@ -0,0 +1,15 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) +add_subdirectory(manager) + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02b/src/core/CMakeLists.txt new file mode 100644 index 00000000..3dbf384c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/src/core/CMakeLists.txt @@ -0,0 +1,25 @@ +# src/core/CMakeLists.txt +message(STATUS "配置核心模块...") + +add_library(core STATIC + registry.f90 + factory_interfaces.f90 + #component_manager.f90 # ← 添加这一行 +) + +target_include_directories(core PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 安装规则 +install(TARGETS core + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) +install(FILES + ${CMAKE_Fortran_MODULE_DIRECTORY}/registry_module.mod + ${CMAKE_Fortran_MODULE_DIRECTORY}/factory_interfaces.mod + DESTINATION include/fortran_cfd/core +) + +message(STATUS "核心模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/02b/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..a960167c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/02b/src/core/registry.f90 new file mode 100644 index 00000000..d620f5ec --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/src/core/registry.f90 @@ -0,0 +1,656 @@ +! src/core/registry.f90 +module registry_module + use, intrinsic :: iso_fortran_env, only: real64 + use factory_interfaces, only: factory_procedure + implicit none + + private + + ! ==================== PUBLIC INTERFACE ==================== + ! 原有接口保持不变 + public :: real64, component_info, component_registry + public :: register_component_simple, initialize_registry, cleanup_registry + public :: has_component, get_available_components + public :: registry_is_initialized, registry_get_size + + ! 新增工厂相关接口 + public :: factory_ptr_type, component_registry_entry + public :: register_component_with_factory + public :: create_component_from_registry + public :: has_factory_component + public :: list_factory_components + + ! ==================== TYPE DEFINITIONS ==================== + + ! 原有类型定义 + type :: component_info + character(len=32) :: category = "" + character(len=32) :: name = "" + integer :: order = 0 + contains + procedure :: print => ci_print + end type component_info + + ! 新增:工厂指针类型 + type :: factory_ptr_type + procedure(factory_procedure), pointer, nopass :: ptr => null() + end type factory_ptr_type + + ! 新增:带工厂的注册条目 + type :: component_registry_entry + character(len=32) :: category = "" + character(len=32) :: name = "" + integer :: order = 0 + type(factory_ptr_type) :: factory + logical :: has_factory = .false. + contains + procedure :: print_with_factory => entry_print + procedure :: can_create => entry_has_factory + end type component_registry_entry + + ! 原有注册表类型(扩展以支持工厂) + type :: component_registry_type + private + ! 简单注册组件(向后兼容) + type(component_info), allocatable :: simple_components(:) + integer :: simple_count = 0 + + ! 带工厂的组件(新增) + type(component_registry_entry), allocatable :: factory_components(:) + integer :: factory_count = 0 + + integer :: capacity = 100 + logical :: verbose = .true. + logical :: initialized = .false. + contains + ! 简单注册方法 + procedure :: register => cr_register ! ← 保持原名 + procedure :: get => cr_get ! ← 保持原名 + procedure :: list_simple => cr_list_simple ! ← 新增别名 + + ! 工厂注册方法 + procedure :: register_with_factory => cr_register_with_factory + procedure :: get_with_factory => cr_get_with_factory + procedure :: list_with_factory => cr_list_with_factory + procedure :: create_from_factory => cr_create_from_factory + + ! 通用方法 + procedure :: clear => cr_clear + procedure :: size => cr_size + procedure :: total_size => cr_total_size + procedure :: is_initialized => cr_is_initialized + end type component_registry_type + + ! Global registry instance + type(component_registry_type), save :: component_registry + +contains + + ! ==================== PUBLIC API ==================== + + ! Initialize registry (保持不变) + subroutine initialize_registry(initial_capacity, verbose) + integer, optional, intent(in) :: initial_capacity + logical, optional, intent(in) :: verbose + + if (component_registry%initialized) then + if (component_registry%verbose) then + print *, "[INFO] Registry already initialized" + end if + return + end if + + if (present(initial_capacity)) then + component_registry%capacity = max(10, initial_capacity) + end if + + if (present(verbose)) then + component_registry%verbose = verbose + end if + + ! Allocate arrays for both types + allocate(component_registry%simple_components(component_registry%capacity)) + allocate(component_registry%factory_components(component_registry%capacity)) + + component_registry%initialized = .true. + component_registry%simple_count = 0 + component_registry%factory_count = 0 + + if (component_registry%verbose) then + print *, "[INIT] Registry initialized, capacity:", component_registry%capacity + print *, " Supports both simple and factory-based registration" + end if + end subroutine initialize_registry + + ! Cleanup registry (保持不变) + subroutine cleanup_registry + call component_registry%clear() + if (component_registry%verbose) then + print *, "[CLEANUP] Registry cleaned up" + end if + end subroutine cleanup_registry + + ! ==================== SIMPLE REGISTRATION (向后兼容) ==================== + + ! Simple registration (no factory) - 保持不变 + subroutine register_component_simple(category, name) + character(len=*), intent(in) :: category, name + + type(component_info) :: info + + info%category = to_lower(trim(adjustl(category))) + info%name = to_lower(trim(adjustl(name))) + info%order = 0 + + call component_registry%register(info) + end subroutine register_component_simple + + ! Check if component exists (简单注册) + function has_component(category, name) result(found) + character(len=*), intent(in) :: category, name + logical :: found + + type(component_info) :: info + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + info = component_registry%get(cat_lower, name_lower) + found = (len_trim(info%category) > 0) + end function has_component + + ! Get available components in a category (简单注册) + subroutine get_available_components(category, names, orders) + character(len=*), intent(in) :: category + character(len=:), allocatable, intent(out), optional :: names(:) + integer, allocatable, intent(out), optional :: orders(:) + + character(len=32) :: cat_lower + integer :: i, count, idx + type(component_info) :: info + + cat_lower = to_lower(trim(adjustl(category))) + + ! Count components in this category + count = 0 + do i = 1, component_registry%simple_count + if (component_registry%simple_components(i)%category == cat_lower) then + count = count + 1 + end if + end do + + ! Allocate arrays if requested + if (present(names)) then + allocate(character(len=32) :: names(count)) + end if + + if (present(orders)) then + allocate(orders(count)) + end if + + ! Fill arrays + idx = 1 + do i = 1, component_registry%simple_count + if (component_registry%simple_components(i)%category == cat_lower) then + info = component_registry%simple_components(i) + if (present(names)) then + names(idx) = info%name + end if + if (present(orders)) then + orders(idx) = info%order + end if + idx = idx + 1 + end if + end do + end subroutine get_available_components + + ! ==================== FACTORY REGISTRATION (新增) ==================== + + ! Register component with factory function + subroutine register_component_with_factory(category, name, factory_func, order) + character(len=*), intent(in) :: category, name + procedure(factory_procedure) :: factory_func + integer, optional, intent(in) :: order + + type(component_registry_entry) :: entry + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + entry%category = cat_lower + entry%name = name_lower + + if (present(order)) then + entry%order = order + else + entry%order = 0 + end if + + entry%factory%ptr => factory_func + entry%has_factory = .true. + + call component_registry%register_with_factory(entry) + end subroutine register_component_with_factory + + ! Create component from registry using factory + function create_component_from_registry(category, name) result(instance) + character(len=*), intent(in) :: category, name + class(*), allocatable :: instance + + character(len=32) :: cat_lower, name_lower + integer :: status + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + call component_registry%create_from_factory(cat_lower, name_lower, instance, status) + + if (status /= 0) then + if (component_registry%verbose) then + print *, "[ERROR] Failed to create component: ", trim(category), ".", trim(name) + end if + end if + end function create_component_from_registry + + ! Check if component has factory + function has_factory_component(category, name) result(found) + character(len=*), intent(in) :: category, name + logical :: found + + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + found = .false. + + if (.not. component_registry%initialized) return + + ! 简化的查找逻辑(实际应调用内部方法) + found = component_registry%factory_count > 0 ! 占位实现 + end function has_factory_component + + ! List all factory components + subroutine list_factory_components(category) + character(len=*), optional, intent(in) :: category + + if (present(category)) then + call component_registry%list_with_factory(category) + else + call component_registry%list_with_factory("") + end if + end subroutine list_factory_components + + ! ==================== PUBLIC UTILITY FUNCTIONS ==================== + + ! Public function to check if registry is initialized + function registry_is_initialized() result(is_initialized) + logical :: is_initialized + is_initialized = component_registry%is_initialized() + end function registry_is_initialized + + ! Public function to get total registry size + function registry_get_size() result(size_val) + integer :: size_val + size_val = component_registry%total_size() + end function registry_get_size + + ! ==================== COMPONENT INFO METHODS ==================== + + subroutine ci_print(this) + class(component_info), intent(in) :: this + + if (this%order > 0) then + print *, " [", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")]" + else + print *, " [", trim(this%category), ".", trim(this%name), "]" + end if + end subroutine ci_print + + subroutine entry_print(this) + class(component_registry_entry), intent(in) :: this + + if (this%has_factory) then + if (this%order > 0) then + print *, " [FACTORY] ", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")" + else + print *, " [FACTORY] ", trim(this%category), ".", trim(this%name) + end if + else + if (this%order > 0) then + print *, " [SIMPLE] ", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")" + else + print *, " [SIMPLE] ", trim(this%category), ".", trim(this%name) + end if + end if + end subroutine entry_print + + logical function entry_has_factory(this) + class(component_registry_entry), intent(in) :: this + entry_has_factory = this%has_factory + end function entry_has_factory + + ! ==================== REGISTRY INTERNAL METHODS ==================== + + ! ---------- Simple Registration Methods ---------- + + subroutine cr_register(this, info) + class(component_registry_type), intent(inout) :: this + type(component_info), intent(in) :: info + + type(component_info), allocatable :: temp(:) + integer :: i + + if (.not. this%initialized) then + error stop "[ERROR] Registry not initialized, call initialize_registry first" + end if + + ! Check if already exists + do i = 1, this%simple_count + if (this%simple_components(i)%category == info%category .and. & + this%simple_components(i)%name == info%name) then + if (this%verbose) then + print *, "[WARN] Overwriting simple component: ", & + trim(info%category), ".", trim(info%name) + end if + this%simple_components(i) = info + return + end if + end do + + ! Expand array if needed + if (this%simple_count >= this%capacity) then + this%capacity = this%capacity * 2 + allocate(temp(this%capacity)) + temp(1:this%simple_count) = this%simple_components(1:this%simple_count) + call move_alloc(temp, this%simple_components) + + if (this%verbose) then + print *, "[INFO] Simple registry expanded to capacity:", this%capacity + end if + end if + + ! Add component + this%simple_count = this%simple_count + 1 + this%simple_components(this%simple_count) = info + + if (this%verbose) then + print *, "[OK] Registered simple: ", trim(info%category), ".", trim(info%name) + end if + end subroutine cr_register + + function cr_get(this, category, name) result(info) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + type(component_info) :: info + + integer :: i + + ! Initialize return value as empty + info%category = "" + info%name = "" + info%order = 0 + + if (.not. this%initialized) then + return + end if + + do i = 1, this%simple_count + if (this%simple_components(i)%category == category .and. & + this%simple_components(i)%name == name) then + info = this%simple_components(i) + return + end if + end do + end function cr_get + + subroutine cr_list_simple(this) + class(component_registry_type), intent(in) :: this + integer :: i + + if (.not. this%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + if (this%simple_count == 0) then + print *, "[INFO] No simple components registered" + return + end if + + print *, "=== Simple Registry Contents (", this%simple_count, " components) ===" + do i = 1, this%simple_count + call this%simple_components(i)%print() + end do + print *, "==========================================" + end subroutine cr_list_simple + + ! ---------- Factory Registration Methods ---------- + + subroutine cr_register_with_factory(this, entry) + class(component_registry_type), intent(inout) :: this + type(component_registry_entry), intent(in) :: entry + + type(component_registry_entry), allocatable :: temp(:) + integer :: i + + if (.not. this%initialized) then + error stop "[ERROR] Registry not initialized, call initialize_registry first" + end if + + ! Check if already exists + do i = 1, this%factory_count + if (this%factory_components(i)%category == entry%category .and. & + this%factory_components(i)%name == entry%name) then + if (this%verbose) then + print *, "[WARN] Overwriting factory component: ", & + trim(entry%category), ".", trim(entry%name) + end if + this%factory_components(i) = entry + return + end if + end do + + ! Expand array if needed + if (this%factory_count >= this%capacity) then + this%capacity = this%capacity * 2 + allocate(temp(this%capacity)) + temp(1:this%factory_count) = this%factory_components(1:this%factory_count) + call move_alloc(temp, this%factory_components) + + if (this%verbose) then + print *, "[INFO] Factory registry expanded to capacity:", this%capacity + end if + end if + + ! Add component + this%factory_count = this%factory_count + 1 + this%factory_components(this%factory_count) = entry + + if (this%verbose) then + print *, "[FACTORY] Registered with factory: ", & + trim(entry%category), ".", trim(entry%name) + end if + end subroutine cr_register_with_factory + + function cr_get_with_factory(this, category, name) result(entry) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + type(component_registry_entry) :: entry + + integer :: i + + ! Initialize return value as empty + entry%category = "" + entry%name = "" + entry%order = 0 + entry%factory%ptr => null() + entry%has_factory = .false. + + if (.not. this%initialized) then + return + end if + + do i = 1, this%factory_count + if (this%factory_components(i)%category == category .and. & + this%factory_components(i)%name == name) then + entry = this%factory_components(i) + return + end if + end do + end function cr_get_with_factory + + subroutine cr_list_with_factory(this, category) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category + + character(len=32) :: cat_lower + integer :: i, count + + if (.not. this%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + cat_lower = to_lower(trim(adjustl(category))) + + if (this%factory_count == 0) then + print *, "[INFO] No factory components registered" + return + end if + + ! Count components in this category + count = 0 + do i = 1, this%factory_count + if (len_trim(cat_lower) == 0 .or. & + this%factory_components(i)%category == cat_lower) then + count = count + 1 + end if + end do + + if (count == 0) then + if (len_trim(cat_lower) > 0) then + print *, "[INFO] No factory components in category: ", trim(cat_lower) + else + print *, "[INFO] No factory components registered" + end if + return + end if + + print *, "=== Factory Registry Contents (", count, " components) ===" + do i = 1, this%factory_count + if (len_trim(cat_lower) == 0 .or. & + this%factory_components(i)%category == cat_lower) then + call this%factory_components(i)%print_with_factory() + end if + end do + print *, "==========================================" + end subroutine cr_list_with_factory + + subroutine cr_create_from_factory(this, category, name, instance, status) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + class(*), allocatable, intent(out) :: instance + integer, intent(out) :: status + + type(component_registry_entry) :: entry + integer :: i + + status = -1 ! Default: not found + + if (.not. this%initialized) then + if (this%verbose) then + print *, "[ERROR] Registry not initialized" + end if + return + end if + + ! Find the entry + do i = 1, this%factory_count + if (this%factory_components(i)%category == category .and. & + this%factory_components(i)%name == name) then + entry = this%factory_components(i) + + if (entry%has_factory .and. associated(entry%factory%ptr)) then + ! Call the factory function + call entry%factory%ptr(instance) + status = 0 ! Success + + if (this%verbose) then + print *, "[FACTORY] Created component: ", & + trim(category), ".", trim(name) + end if + else + status = -2 ! No factory function + if (this%verbose) then + print *, "[ERROR] No factory function for: ", & + trim(category), ".", trim(name) + end if + end if + return + end if + end do + + ! If we get here, component not found + if (this%verbose) then + print *, "[ERROR] Factory component not found: ", & + trim(category), ".", trim(name) + end if + end subroutine cr_create_from_factory + + ! ---------- Common Methods ---------- + + subroutine cr_clear(this) + class(component_registry_type), intent(inout) :: this + + if (allocated(this%simple_components)) then + deallocate(this%simple_components) + end if + + if (allocated(this%factory_components)) then + deallocate(this%factory_components) + end if + + this%simple_count = 0 + this%factory_count = 0 + this%capacity = 100 + this%initialized = .false. + end subroutine cr_clear + + integer function cr_total_size(this) + class(component_registry_type), intent(in) :: this + cr_total_size = this%simple_count + this%factory_count + end function cr_total_size + + integer function cr_size(this) + class(component_registry_type), intent(in) :: this + cr_size = this%simple_count ! Backward compatibility + end function cr_size + + logical function cr_is_initialized(this) + class(component_registry_type), intent(in) :: this + cr_is_initialized = this%initialized + end function cr_is_initialized + + ! ==================== UTILITY FUNCTIONS ==================== + + function to_lower(str) result(lower_str) + character(len=*), intent(in) :: str + character(len=len(str)) :: lower_str + integer :: i + + do i = 1, len(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + lower_str(i:i) = char(ichar(str(i:i)) + 32) + else + lower_str(i:i) = str(i:i) + end if + end do + end function to_lower + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02b/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..46964ce7 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/src/infrastructure/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "配置基础设施模块...") + +add_library(infrastructure STATIC + config.f90 + mesh.f90 +) + +target_link_libraries(infrastructure PRIVATE core) + +target_include_directories(infrastructure PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS infrastructure + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "基础设施模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/02b/src/infrastructure/config.f90 new file mode 100644 index 00000000..d3b1e0df --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/src/infrastructure/config.f90 @@ -0,0 +1,90 @@ +! src/infrastructure/config.f90 +module config_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, cfd_config, config_print, config_with_reconstruction + + ! CFD configuration type + type :: cfd_config + character(len=20) :: ic_type = "step" + character(len=20) :: recon_scheme = "eno" + character(len=20) :: flux_type = "rusanov" + integer :: rk_order = 1 + real(real64) :: wave_speed = 1.0_real64 + real(real64) :: final_time = 0.625_real64 + real(real64) :: dt = 0.025_real64 + character(len=20) :: boundary_type = "periodic" + real(real64) :: left_boundary_value = 1.0_real64 + real(real64) :: right_boundary_value = 2.0_real64 + integer :: spatial_order = 2 + logical :: verbose = .true. + end type cfd_config + +contains + + subroutine config_print(this) + type(cfd_config), intent(in) :: this + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(this%ic_type) + print *, "Reconstruction: ", trim(this%recon_scheme), " (order:", this%spatial_order, ")" + print *, "Flux type: ", trim(this%flux_type) + print *, "Time integration: RK", this%rk_order + print *, "Wave speed: ", this%wave_speed + print *, "Final time: ", this%final_time + print *, "Time step: ", this%dt + print *, "Boundary: ", trim(this%boundary_type) + if (trim(this%boundary_type) == 'dirichlet') then + print *, " Dirichlet values: [", this%left_boundary_value, ", ", & + this%right_boundary_value, "]" + end if + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(this, scheme, order) + type(cfd_config), intent(inout) :: this + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + character(len=20) :: scheme_lower + + ! Convert to lowercase + scheme_lower = scheme + call to_lower_inplace(scheme_lower) + this%recon_scheme = trim(adjustl(scheme_lower)) + + ! Set order + if (present(order)) then + this%spatial_order = order + else + ! Smart defaults + if (index(this%recon_scheme, 'weno') > 0) then + this%spatial_order = 5 + else if (trim(this%recon_scheme) == 'eno') then + this%spatial_order = 3 + else + print *, "[ERROR] Unsupported reconstruction scheme: ", trim(this%recon_scheme) + return + end if + end if + + if (this%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(this%recon_scheme), & + " Order: ", this%spatial_order + end if + end subroutine config_with_reconstruction + + subroutine to_lower_inplace(str) + character(len=*), intent(inout) :: str + integer :: i + + do i = 1, len_trim(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + str(i:i) = char(ichar(str(i:i)) + 32) + end if + end do + end subroutine to_lower_inplace + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/02b/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..896969bd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/src/infrastructure/mesh.f90 @@ -0,0 +1,74 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, mesh_type, mesh_init, mesh_print_info + + ! mesh + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer :: ncells = 40 + integer :: nnodes + integer :: nx + real(wp), allocatable :: x(:) ! + real(wp), allocatable :: xcc(:) ! + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer, optional, intent(in) :: ncells + + integer :: i + + ! Set + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! computation + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! node + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! cell + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== ===" + print *, ": [", this%xmin, ", ", this%xmax, "]" + print *, ": ", this%ncells + print *, ": ", this%nnodes + print *, " dx: ", this%dx + print *, " L: ", this%L + print *, "==========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/src/manager/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02b/src/manager/CMakeLists.txt new file mode 100644 index 00000000..00c8bf49 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/src/manager/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/manager/CMakeLists.txt +message(STATUS "配置管理器模块...") + +# 创建管理器库 +add_library(manager STATIC + component_manager.f90 + component_factory.f90 +) + +# 明确依赖关系:管理器依赖所有其他模块 +target_link_libraries(manager + PRIVATE + core + infrastructure + reconstructor + flux +) + +# 设置模块输出目录 +set_target_properties(manager PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "管理器模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/src/manager/component_factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/02b/src/manager/component_factory.f90 new file mode 100644 index 00000000..114fedea --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/src/manager/component_factory.f90 @@ -0,0 +1,127 @@ +! src/manager/component_factory.f90 +module component_factory_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + use eno_reconstructor_module, only: eno_reconstructor, create_eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor, create_weno3_reconstructor + use rusanov_flux_module, only: rusanov_flux, create_rusanov_flux + + implicit none + private + public :: wp, create_reconstructor, create_flux_calculator + + ! 错误代码 + integer, parameter :: CM_SUCCESS = 0 + integer, parameter :: CM_ERROR_UNKNOWN_SCHEME = 1 + integer, parameter :: CM_ERROR_UNKNOWN_FLUX = 2 + integer, parameter :: CM_ERROR_INVALID_ORDER = 3 + +contains + + ! ==================== 重构器创建 ==================== + + function create_reconstructor(config, status) result(recon) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(reconstructor_base), allocatable :: recon + + character(len=20) :: scheme + integer :: order, error_code + + scheme = trim(adjustl(config%recon_scheme)) + order = config%spatial_order + + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating reconstructor: ", scheme, " order=", order + end if + + select case(scheme) + case('eno') + allocate(eno_reconstructor :: recon) + select type(recon) + type is(eno_reconstructor) + recon = create_eno_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case('weno3') + allocate(weno3_reconstructor :: recon) + select type(recon) + type is(weno3_reconstructor) + recon = create_weno3_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case default + error_code = CM_ERROR_UNKNOWN_SCHEME + if (config%verbose) then + print *, "[ERROR] Unknown reconstructor scheme: ", scheme + print *, " Available: eno, weno3" + end if + end select + + ! 检查阶数有效性 + if (error_code == CM_SUCCESS) then + if (order < 1) then + error_code = CM_ERROR_INVALID_ORDER + if (config%verbose) then + print *, "[ERROR] Invalid spatial order: ", order + end if + end if + end if + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Reconstructor creation failed" + end if + end function create_reconstructor + + ! ==================== 通量计算器创建 ==================== + + function create_flux_calculator(config, status) result(flux) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(flux_calculator_base), allocatable :: flux + + character(len=20) :: flux_type + integer :: error_code + + flux_type = trim(adjustl(config%flux_type)) + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating flux calculator: ", flux_type + end if + + select case(flux_type) + case('rusanov') + allocate(rusanov_flux :: flux) + select type(flux) + type is(rusanov_flux) + flux = create_rusanov_flux() + flux%wave_speed_default = config%wave_speed + end select + + case default + error_code = CM_ERROR_UNKNOWN_FLUX + if (config%verbose) then + print *, "[ERROR] Unknown flux type: ", flux_type + print *, " Available: rusanov" + end if + end select + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Flux calculator creation failed" + end if + end function create_flux_calculator + +end module component_factory_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/src/manager/component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/02b/src/manager/component_manager.f90 new file mode 100644 index 00000000..9e095c25 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/src/manager/component_manager.f90 @@ -0,0 +1,75 @@ +! src/manager/component_manager.f90 +module component_manager_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use component_factory_module, only: create_reconstructor, create_flux_calculator + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + implicit none + private + public :: wp, component_manager_info, validate_config + public :: create_reconstructor, create_flux_calculator + +contains + + ! ==================== 配置验证 ==================== + + function validate_config(config) result(is_valid) + type(cfd_config), intent(in) :: config + logical :: is_valid + + integer :: status + class(reconstructor_base), allocatable :: test_recon + class(flux_calculator_base), allocatable :: test_flux + + is_valid = .false. + + ! 测试创建重构器 + test_recon = create_reconstructor(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid reconstructor configuration" + end if + return + end if + + ! 测试创建通量计算器 + test_flux = create_flux_calculator(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid flux configuration" + end if + return + end if + + ! 清理测试组件 + if (allocated(test_recon)) deallocate(test_recon) + if (allocated(test_flux)) deallocate(test_flux) + + is_valid = .true. + + if (config%verbose) then + print *, "[CONFIG VALIDATION] Configuration is valid" + end if + end function validate_config + + ! ==================== 信息显示 ==================== + + subroutine component_manager_info() + print *, "=== Component Manager ===" + print *, "Available reconstructors:" + print *, " - eno (orders: 1-7)" + print *, " - weno3 (order: 3)" + print *, "" + print *, "Available flux calculators:" + print *, " - rusanov" + print *, "" + print *, "Features:" + print *, " - Configuration validation" + print *, " - Component creation from config" + print *, " - Error handling with status codes" + print *, "=========================" + end subroutine component_manager_info + +end module component_manager_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02b/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02b/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..3fde7746 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,28 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux STATIC + base.f90 + rusanov.f90 +) + +#target_link_libraries(flux PRIVATE core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 明确设置不使用 /Qm64 选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + set_target_properties(flux PROPERTIES + Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /Qm64" # 明确设置,避免继承问题 + ) +endif() + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/02b/src/numerics/flux/base.f90 new file mode 100644 index 00000000..7080a7ae --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/src/numerics/flux/base.f90 @@ -0,0 +1,30 @@ +!src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + procedure :: print_basic_info => flux_print_basic ! 添加辅助方法 + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + + subroutine flux_print_basic(this) + class(flux_calculator_base), intent(in) :: this + print *, " Name: ", trim(this%name) + end subroutine flux_print_basic + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/02b/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..daa9e3bb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/src/numerics/flux/rusanov.f90 @@ -0,0 +1,41 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux, create_rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + + ! 构造函数接口 + interface rusanov_flux + module procedure create_rusanov_flux + end interface + +contains + + ! 构造函数 + type(rusanov_flux) function create_rusanov_flux() result(this) + this%name = "Rusanov" + this%wave_speed_default = 1.0_real64 + end function create_rusanov_flux + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Flux calculator information:" + call this%print_basic_info() + + ! 添加Rusanov特有信息 + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02b/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..5e4b938d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,22 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor STATIC + base.f90 + eno.f90 + weno3.f90 +) + +target_link_libraries(reconstructor PRIVATE core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/02b/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..53798d02 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/src/numerics/reconstructor/base.f90 @@ -0,0 +1,33 @@ +!src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + procedure :: print_basic_info => reconstructor_print_basic ! 添加一个辅助方法 + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + + subroutine reconstructor_print_basic(this) + class(reconstructor_base), intent(in) :: this + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_print_basic + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/02b/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..f973e8b3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor, create_eno_reconstructor ! ← 添加这个 + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + + ! 构造函数接口 + interface eno_reconstructor + module procedure create_eno_reconstructor + end interface + +contains + + ! 构造函数 + type(eno_reconstructor) function create_eno_reconstructor() result(this) + this%name = "ENO" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_eno_reconstructor + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加ENO特有信息 + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/02b/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..d5b7a747 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno3_reconstructor, create_weno3_reconstructor + + type, extends(reconstructor_base) :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + + ! 构造函数接口 + interface weno3_reconstructor + module procedure create_weno3_reconstructor + end interface + +contains + + ! 构造函数 + type(weno3_reconstructor) function create_weno3_reconstructor() result(this) + this%name = "WENO3" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_weno3_reconstructor + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO3特有信息 + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02b/tests/CMakeLists.txt new file mode 100644 index 00000000..1008c1b5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/tests/CMakeLists.txt @@ -0,0 +1,42 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +add_executable(test_minimal_simple test_minimal_simple.f90) + +message(STATUS "CMAKE_Fortran_MODULE_DIRECTORY=${CMAKE_Fortran_MODULE_DIRECTORY}") + +#target_include_directories( test_minimal_simple +# PRIVATE +# ${CMAKE_Fortran_MODULE_DIRECTORY} +#) + +target_link_libraries( test_minimal_simple + PRIVATE + infrastructure +) + +add_executable(test_simple_link test_simple_link.f90) +target_link_libraries(test_simple_link + PRIVATE + reconstructor + flux +) + + +add_executable(test_factory_simple test_factory_simple.f90) +target_link_libraries(test_factory_simple + PRIVATE + core + infrastructure + reconstructor + flux +) + +# 更新测试链接 +add_executable(test_component_manager test_component_manager.f90) + +target_link_libraries(test_component_manager + PRIVATE + manager # ← 链接到新的管理器库 + infrastructure +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/tests/test_component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/02b/tests/test_component_manager.f90 new file mode 100644 index 00000000..f60c3505 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/tests/test_component_manager.f90 @@ -0,0 +1,111 @@ +! tests/test_component_manager.f90 +program test_component_manager + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use component_manager_module, only: create_reconstructor, create_flux_calculator + use component_manager_module, only: component_manager_info, validate_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + implicit none + + type(cfd_config) :: config + class(reconstructor_base), allocatable :: recon + class(flux_calculator_base), allocatable :: flux + integer :: status + logical :: is_valid + + print *, "=== Component Manager Test ===" + print *, "" + + ! 显示组件管理器信息 + call component_manager_info() + print *, "" + + ! 测试1: 基本配置 + print *, "1. Testing basic ENO3 + Rusanov configuration..." + print *, "-----------------------------------------------" + + config%verbose = .true. + call config_print(config) + + ! 配置ENO3重构 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + call config_print(config) + print *, "" + + ! 验证配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Configuration is valid" + else + print *, "[ERROR] Configuration is invalid" + end if + print *, "" + + ! 测试2: 创建组件 + print *, "2. Testing component creation..." + print *, "--------------------------------" + + ! 创建重构器(带状态检查) + recon = create_reconstructor(config, status) + if (status == 0) then + print *, "[OK] Reconstructor created successfully" + call recon%info() + else + print *, "[ERROR] Failed to create reconstructor, code:", status + end if + print *, "" + + ! 创建通量计算器 + flux = create_flux_calculator(config, status) + if (status == 0) then + print *, "[OK] Flux calculator created successfully" + call flux%info() + else + print *, "[ERROR] Failed to create flux calculator, code:", status + end if + print *, "" + + ! 测试3: WENO3重构测试 + print *, "3. Testing WENO3 configuration..." + print *, "---------------------------------" + + call config_with_reconstruction(config, "weno3", 3) + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] WENO3 configuration is valid" + + ! 创建WENO3重构器 + recon = create_reconstructor(config) + call recon%info() + else + print *, "[ERROR] WENO3 configuration is invalid" + end if + print *, "" + + ! 测试4: 错误配置测试 + print *, "4. Testing invalid configuration..." + print *, "-----------------------------------" + + config%recon_scheme = "unknown_scheme" + config%flux_type = "unknown_flux" + + is_valid = validate_config(config) + if (.not. is_valid) then + print *, "[OK] Invalid configuration correctly rejected" + else + print *, "[ERROR] Invalid configuration should have been rejected" + end if + + ! 清理 + if (allocated(recon)) deallocate(recon) + if (allocated(flux)) deallocate(flux) + + print *, "" + print *, "=== Component manager test completed successfully ===" + +end program test_component_manager \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/tests/test_factory_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/02b/tests/test_factory_simple.f90 new file mode 100644 index 00000000..d4139bb1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/tests/test_factory_simple.f90 @@ -0,0 +1,86 @@ +!tests/test_factory_simple.f90 +program test_factory_simple + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module, only: initialize_registry, cleanup_registry, & + register_component_simple, has_component + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Basic systems + print *, "1. Testing basic systems..." + print *, "-----------------------------" + call config_print(config) + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Creating reconstructors + print *, "2. Testing reconstructors..." + print *, "------------------------------" + + ! 创建并测试ENO重构器 + print *, "Creating ENO reconstructor..." + eno = eno_reconstructor() ! 使用构造函数 + call eno%info() ! 必须调用info方法 + + print *, "" + print *, "Creating WENO3 reconstructor..." + weno3 = weno3_reconstructor() ! 使用构造函数 + call weno3%info() ! 必须调用info方法 + print *, "" + + ! Test 3: Creating flux calculator + print *, "3. Testing flux calculator..." + print *, "-------------------------------" + + print *, "Creating Rusanov flux calculator..." + rusanov = rusanov_flux() ! 使用构造函数 + call rusanov%info() ! 必须调用info方法 + print *, "" + + ! Test 4: Registry integration + print *, "4. Testing registry..." + print *, "----------------------" + + call initialize_registry(verbose=.true.) + + ! Register components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! Check registration + if (has_component("reconstructor", "eno")) then + print *, "[OK] ENO reconstructor registered successfully" + end if + + if (has_component("reconstructor", "weno3")) then + print *, "[OK] WENO3 reconstructor registered successfully" + end if + + if (has_component("flux", "rusanov")) then + print *, "[OK] Rusanov flux registered successfully" + end if + + print *, "" + call cleanup_registry() + + print *, "=== Factory pattern simple test completed successfully ===" + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/tests/test_minimal_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/02b/tests/test_minimal_simple.f90 new file mode 100644 index 00000000..cc6753b5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/tests/test_minimal_simple.f90 @@ -0,0 +1,84 @@ +! tests/test_minimal_simple.f90 +program test_minimal_simple + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Simple Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_simple() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components (simple version) + print *, "5. Testing registry functionality" + print *, "---------------------------------" + print *, "Registry initialized: ", registry_is_initialized() + print *, "Number of components: ", registry_get_size() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Simple test completed successfully ===" + +end program test_minimal_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02b/tests/test_simple_link.f90 b/example/1d-linear-convection/weno3/fortran/registry/02b/tests/test_simple_link.f90 new file mode 100644 index 00000000..27b165b0 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02b/tests/test_simple_link.f90 @@ -0,0 +1,108 @@ +! tests/test_minimal.f90 +program test_minimal + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i ! Declare loop variable here + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_simple() + print *, "Registry size: ", component_registry%size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components + print *, "5. Testing available components" + print *, "--------------------------------" + + ! Use a separate block to avoid allocation issues + call test_available_components() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Minimal test completed successfully ===" + +contains + + ! Internal subroutine for testing available components + subroutine test_available_components() + character(len=:), allocatable :: names(:) + integer, allocatable :: orders(:) + integer :: j + + call get_available_components("reconstructor", names, orders) + + if (allocated(names)) then + print *, "Reconstructors:" + do j = 1, size(names) + print *, " - ", trim(names(j)) + if (allocated(orders)) then + print *, " Order: ", orders(j) + end if + end do + else + print *, "No reconstructors found" + end if + end subroutine test_available_components + +end program test_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02c/CMakeLists.txt new file mode 100644 index 00000000..ef66d584 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/README.md b/example/1d-linear-convection/weno3/fortran/registry/02c/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/02c/scripts/build.bat new file mode 100644 index 00000000..6fd6dc03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/scripts/build.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Project Builder +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python build script with full Intel environment support... +echo. + +python build.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Build failed + pause + exit /b 1 +) + +echo. +echo [INFO] Build completed successfully! +echo. +echo [INFO] To run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/02c/scripts/build.py new file mode 100644 index 00000000..3bf6d537 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/scripts/build.py @@ -0,0 +1,629 @@ +#!/usr/bin/env python3 +""" +Fortran CFD Project Builder - 完整Python解决方案 +在Python内部处理Intel oneAPI环境配置 +""" + +import os +import sys +import subprocess +import shutil +import argparse +import time +import platform +import tempfile +from pathlib import Path + +class IntelEnvironment: + """Intel oneAPI环境管理器""" + + def __init__(self): + self.setvars_path = None + self.env_vars = {} + + def find_setvars(self): + """查找setvars.bat文件""" + possible_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + r"C:\Program Files (x86)\Intel\oneAPI\compiler\latest\env\vars.bat", + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\setvars.bat"), + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\compiler\latest\env\vars.bat"), + ] + + for path in possible_paths: + if os.path.exists(path): + self.setvars_path = path + return True + + return False + + def setup_environment(self): + """设置Intel环境""" + if not self.find_setvars(): + return False + + try: + # 创建临时的批处理文件来捕获环境变量 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + f.write(f'@echo off\n') + f.write(f'call "{self.setvars_path}" >nul 2>&1\n') + f.write(f'set\n') # 输出所有环境变量 + temp_bat = f.name + + # 运行批处理文件并捕获输出 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + shell=True + ) + + # 解析环境变量 + for line in result.stdout.split('\n'): + line = line.strip() + if '=' in line: + key, value = line.split('=', 1) + self.env_vars[key.strip()] = value.strip() + + # 清理临时文件 + os.unlink(temp_bat) + + # 更新当前进程的环境变量 + os.environ.update(self.env_vars) + + return True + + except Exception as e: + print(f"设置Intel环境失败: {e}") + return False + + def get_compiler_info(self): + """获取编译器信息""" + info = {} + + # 检查ifx编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + env={**os.environ, **self.env_vars} if self.env_vars else os.environ + ) + + if result.returncode == 0: + for line in result.stdout.split('\n'): + if 'Version' in line or '版本' in line: + info['ifx_version'] = line.strip() + break + except: + pass + + # 检查环境变量 + info['ifx_root'] = self.env_vars.get('IFX_ROOT', '') + info['compiler_root'] = self.env_vars.get('ONEAPI_ROOT', '') + + return info + +class BuildSystem: + """构建系统主类""" + + def __init__(self): + self.project_root = Path(__file__).parent.parent + self.build_dir = self.project_root / "build" + self.intel_env = IntelEnvironment() + + # 设置控制台编码 + if sys.platform == "win32": + try: + import ctypes + # 设置控制台输出为UTF-8 + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + except: + pass + + def print_header(self, text): + """打印标题""" + print(f"\n{'='*70}") + print(f" {text}") + print(f"{'='*70}\n") + + def print_step(self, step, total, message): + """打印步骤""" + print(f"[{step}/{total}] {message}...") + + def print_success(self, message): + """打印成功""" + print(f"\033[92m✓ {message}\033[0m") + + def print_error(self, message): + """打印错误""" + print(f"\033[91m✗ {message}\033[0m") + + def print_warning(self, message): + """打印警告""" + print(f"\033[93m! {message}\033[0m") + + def print_info(self, message): + """打印信息""" + print(f"\033[94mℹ {message}\033[0m") + + def check_prerequisites(self): + """检查前提条件""" + self.print_step(1, 6, "检查前提条件") + + # 检查Python版本 + python_version = sys.version.split()[0] + self.print_info(f"Python版本: {python_version}") + + # 检查平台 + self.print_info(f"平台: {platform.system()} {platform.release()}") + self.print_info(f"处理器核心数: {os.cpu_count()}") + + # 检查CMake + try: + result = subprocess.run( + ["cmake", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + version_line = result.stdout.split('\n')[0] + self.print_success(f"CMake: {version_line}") + else: + self.print_error("CMake未找到") + return False + except FileNotFoundError: + self.print_error("CMake未安装") + return False + + return True + + def setup_intel_environment(self, args): + """设置Intel环境""" + self.print_step(2, 6, "配置Intel oneAPI环境") + + if not self.intel_env.find_setvars(): + self.print_warning("未找到Intel oneAPI setvars.bat") + self.print_info("将尝试使用系统环境中的编译器") + + # 检查是否能直接访问编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + self.print_success("Intel编译器在系统PATH中找到") + return True + else: + self.print_warning("Intel编译器未在PATH中找到") + except: + self.print_warning("无法访问Intel编译器") + + return True # 继续,让CMake自己找编译器 + + # 设置环境 + if self.intel_env.setup_environment(): + compiler_info = self.intel_env.get_compiler_info() + + if compiler_info.get('ifx_version'): + self.print_success(f"Intel Fortran编译器: {compiler_info['ifx_version']}") + elif compiler_info.get('ifx_root'): + self.print_success(f"Intel编译器路径: {compiler_info['ifx_root']}") + else: + self.print_success("Intel oneAPI环境配置完成") + + return True + else: + self.print_warning("Intel环境配置失败,将继续使用系统环境") + return True + + def clean_build_directory(self, args): + """清理构建目录""" + if args.clean and self.build_dir.exists(): + self.print_info("清理构建目录...") + try: + shutil.rmtree(self.build_dir) + self.print_success("构建目录已清理") + except Exception as e: + self.print_error(f"清理失败: {e}") + if not args.force: + return False + return True + + def run_command(self, cmd, cwd=None, check=True, env=None): + """运行命令""" + if isinstance(cmd, list): + cmd_str = ' '.join(str(c) for c in cmd if c) + else: + cmd_str = str(cmd) + + print(f" \033[96m$\033[0m {cmd_str}") + + try: + # 合并环境变量 + exec_env = os.environ.copy() + if env: + exec_env.update(env) + if self.intel_env.env_vars: + exec_env.update(self.intel_env.env_vars) + + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=False, + env=exec_env + ) + + # 处理输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or '完成' in line or '生成' in line: + print(f" \033[92m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + if check and result.returncode != 0: + self.print_error(f"命令执行失败,退出码: {result.returncode}") + return False + + return True + + except Exception as e: + self.print_error(f"命令执行异常: {e}") + return False + + def configure_cmake(self, args): + """配置CMake""" + self.print_step(3, 6, "配置CMake项目") + + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ] + + if args.compiler == "ifx": + cmake_cmd.extend(["-T", "fortran=ifx"]) + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + success = self.run_command(cmake_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("CMake配置完成") + else: + self.print_error("CMake配置失败") + + return success + + def build_project(self, args): + """构建项目""" + self.print_step(4, 6, "构建项目") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + success = self.run_command(build_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("项目构建完成") + else: + self.print_error("构建失败") + + return success + + def run_tests_with_environment(self, test_exe): + """运行单个测试,确保有Intel环境""" + try: + # 创建临时的批处理文件来运行测试 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'"{test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'"{test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + return result + + except Exception as e: + print(f"运行测试失败: {e}") + return None + + def run_tests(self, args): + """运行测试""" + self.print_step(5, 6, "运行测试") + + # 查找测试可执行文件 + test_dir = self.build_dir / "bin" / args.build_type + if not test_dir.exists(): + test_dir = self.build_dir / "bin" + if not test_dir.exists(): + test_dir = self.build_dir + + test_files = list(test_dir.glob("test_*.exe")) + + if not test_files: + self.print_warning("未找到测试程序") + return True + + all_passed = True + + for test_exe in sorted(test_files): + test_name = test_exe.stem + self.print_info(f"运行测试: {test_name}") + print(f" {'-'*50}") + + # 运行测试 + result = self.run_tests_with_environment(str(test_exe)) + + if result is None: + self.print_error(f" {test_name} 运行失败") + all_passed = False + continue + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + print(f" {line}") + + if result.returncode == 0: + self.print_success(f" {test_name} 通过") + else: + self.print_error(f" {test_name} 失败 (退出码: {result.returncode})") + all_passed = False + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + print() # 空行 + + return all_passed + + def create_test_runner(self, args): + """创建独立的测试运行器""" + self.print_step(6, 6, "创建测试运行器") + + runner_path = self.build_dir / "run_tests.bat" + + content = f'''@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Test Runner +echo ======================================== +echo. + +REM Setup Intel oneAPI environment +set "SETVARS_PATH={self.intel_env.setvars_path or ''}" +if exist "%SETVARS_PATH%" ( + call "%SETVARS_PATH%" >nul + echo [INFO] Intel environment configured +) else ( + echo [WARNING] Intel environment not found + echo [WARNING] Tests may fail without runtime libraries +) + +echo. + +REM Run all test executables +set "TEST_COUNT=0" +set "PASS_COUNT=0" + +for %%f in ("bin\\{args.build_type}\\test_*.exe") do ( + set /a TEST_COUNT+=1 + echo [TEST %%f] + echo {'-'*50} + + %%f + if errorlevel 1 ( + echo [FAILED] %%f + ) else ( + echo [PASSED] %%f + set /a PASS_COUNT+=1 + ) + echo. +) + +echo ======================================== +echo Tests: %PASS_COUNT%/%TEST_COUNT% passed +if %PASS_COUNT% equ %TEST_COUNT% ( + echo [SUCCESS] All tests passed! +) else ( + echo [FAILURE] Some tests failed +) +echo ======================================== + +pause +''' + + with open(runner_path, 'w', encoding='utf-8') as f: + f.write(content) + + self.print_success(f"测试运行器已创建: {runner_path}") + self.print_info(f"使用方法: cd build && run_tests.bat") + + return runner_path + + def generate_report(self, args, build_time, tests_passed): + """生成构建报告""" + self.print_header("构建完成") + + print(f"项目: {self.project_root.name}") + print(f"构建类型: {args.build_type}") + print(f"编译器: {args.compiler}") + print(f"并行作业: {args.jobs}") + print(f"总耗时: {build_time:.1f}秒") + print(f"测试结果: {'全部通过' if tests_passed else '有失败'}") + + # 显示生成的可执行文件 + bin_dir = self.build_dir / "bin" / args.build_type + if bin_dir.exists(): + print(f"\n生成的可执行文件:") + for exe in sorted(bin_dir.glob("*.exe")): + size_mb = exe.stat().st_size / (1024 * 1024) + print(f" • {exe.name} ({size_mb:.2f} MB)") + + # 显示测试运行器信息 + runner_path = self.build_dir / "run_tests.bat" + if runner_path.exists(): + print(f"\n独立测试运行器:") + print(f" • {runner_path.name}") + print(f" 在Intel oneAPI环境中运行所有测试") + + def run(self): + """运行构建系统""" + parser = argparse.ArgumentParser( + description="Fortran CFD项目构建工具", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认构建 + %(prog)s --clean # 清理后构建 + %(prog)s --build-type Release # Release构建 + %(prog)s --no-tests # 只构建,不运行测试 + %(prog)s -j8 --verbose # 8线程并行构建,详细输出 + """ + ) + + parser.add_argument("--build-type", choices=["Debug", "Release"], + default="Debug", help="构建类型") + parser.add_argument("--compiler", choices=["ifx", "ifort"], + default="ifx", help="Fortran编译器") + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-tests", action="store_true", + help="跳过测试") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + + args = parser.parse_args() + + # 开始构建 + start_time = time.time() + + self.print_header("Fortran CFD 项目构建系统") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 检查前提条件 + if not self.check_prerequisites(): + if not args.force: + return 1 + + # 2. 设置Intel环境 + if not self.setup_intel_environment(args): + if not args.force: + return 1 + + # 3. 清理目录 + if not self.clean_build_directory(args): + if not args.force: + return 1 + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 4. 配置CMake + if not self.configure_cmake(args): + if not args.force: + return 1 + + # 5. 构建项目 + if not self.build_project(args): + if not args.force: + return 1 + + # 6. 运行测试和创建测试运行器 + tests_passed = True + if not args.no_tests: + tests_passed = self.run_tests(args) + + # 创建测试运行器 + self.create_test_runner(args) # 传递 args 参数 + + # 7. 生成报告 + build_time = time.time() - start_time + self.generate_report(args, build_time, tests_passed) + + return 0 if tests_passed else 1 + + except KeyboardInterrupt: + self.print_error("\n构建被用户中断") + return 1 + except Exception as e: + self.print_error(f"构建过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + builder = BuildSystem() + return builder.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/02c/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02c/src/CMakeLists.txt new file mode 100644 index 00000000..f958422d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/src/CMakeLists.txt @@ -0,0 +1,15 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) +add_subdirectory(manager) + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02c/src/core/CMakeLists.txt new file mode 100644 index 00000000..3dbf384c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/src/core/CMakeLists.txt @@ -0,0 +1,25 @@ +# src/core/CMakeLists.txt +message(STATUS "配置核心模块...") + +add_library(core STATIC + registry.f90 + factory_interfaces.f90 + #component_manager.f90 # ← 添加这一行 +) + +target_include_directories(core PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 安装规则 +install(TARGETS core + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) +install(FILES + ${CMAKE_Fortran_MODULE_DIRECTORY}/registry_module.mod + ${CMAKE_Fortran_MODULE_DIRECTORY}/factory_interfaces.mod + DESTINATION include/fortran_cfd/core +) + +message(STATUS "核心模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/02c/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..a960167c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/02c/src/core/registry.f90 new file mode 100644 index 00000000..d620f5ec --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/src/core/registry.f90 @@ -0,0 +1,656 @@ +! src/core/registry.f90 +module registry_module + use, intrinsic :: iso_fortran_env, only: real64 + use factory_interfaces, only: factory_procedure + implicit none + + private + + ! ==================== PUBLIC INTERFACE ==================== + ! 原有接口保持不变 + public :: real64, component_info, component_registry + public :: register_component_simple, initialize_registry, cleanup_registry + public :: has_component, get_available_components + public :: registry_is_initialized, registry_get_size + + ! 新增工厂相关接口 + public :: factory_ptr_type, component_registry_entry + public :: register_component_with_factory + public :: create_component_from_registry + public :: has_factory_component + public :: list_factory_components + + ! ==================== TYPE DEFINITIONS ==================== + + ! 原有类型定义 + type :: component_info + character(len=32) :: category = "" + character(len=32) :: name = "" + integer :: order = 0 + contains + procedure :: print => ci_print + end type component_info + + ! 新增:工厂指针类型 + type :: factory_ptr_type + procedure(factory_procedure), pointer, nopass :: ptr => null() + end type factory_ptr_type + + ! 新增:带工厂的注册条目 + type :: component_registry_entry + character(len=32) :: category = "" + character(len=32) :: name = "" + integer :: order = 0 + type(factory_ptr_type) :: factory + logical :: has_factory = .false. + contains + procedure :: print_with_factory => entry_print + procedure :: can_create => entry_has_factory + end type component_registry_entry + + ! 原有注册表类型(扩展以支持工厂) + type :: component_registry_type + private + ! 简单注册组件(向后兼容) + type(component_info), allocatable :: simple_components(:) + integer :: simple_count = 0 + + ! 带工厂的组件(新增) + type(component_registry_entry), allocatable :: factory_components(:) + integer :: factory_count = 0 + + integer :: capacity = 100 + logical :: verbose = .true. + logical :: initialized = .false. + contains + ! 简单注册方法 + procedure :: register => cr_register ! ← 保持原名 + procedure :: get => cr_get ! ← 保持原名 + procedure :: list_simple => cr_list_simple ! ← 新增别名 + + ! 工厂注册方法 + procedure :: register_with_factory => cr_register_with_factory + procedure :: get_with_factory => cr_get_with_factory + procedure :: list_with_factory => cr_list_with_factory + procedure :: create_from_factory => cr_create_from_factory + + ! 通用方法 + procedure :: clear => cr_clear + procedure :: size => cr_size + procedure :: total_size => cr_total_size + procedure :: is_initialized => cr_is_initialized + end type component_registry_type + + ! Global registry instance + type(component_registry_type), save :: component_registry + +contains + + ! ==================== PUBLIC API ==================== + + ! Initialize registry (保持不变) + subroutine initialize_registry(initial_capacity, verbose) + integer, optional, intent(in) :: initial_capacity + logical, optional, intent(in) :: verbose + + if (component_registry%initialized) then + if (component_registry%verbose) then + print *, "[INFO] Registry already initialized" + end if + return + end if + + if (present(initial_capacity)) then + component_registry%capacity = max(10, initial_capacity) + end if + + if (present(verbose)) then + component_registry%verbose = verbose + end if + + ! Allocate arrays for both types + allocate(component_registry%simple_components(component_registry%capacity)) + allocate(component_registry%factory_components(component_registry%capacity)) + + component_registry%initialized = .true. + component_registry%simple_count = 0 + component_registry%factory_count = 0 + + if (component_registry%verbose) then + print *, "[INIT] Registry initialized, capacity:", component_registry%capacity + print *, " Supports both simple and factory-based registration" + end if + end subroutine initialize_registry + + ! Cleanup registry (保持不变) + subroutine cleanup_registry + call component_registry%clear() + if (component_registry%verbose) then + print *, "[CLEANUP] Registry cleaned up" + end if + end subroutine cleanup_registry + + ! ==================== SIMPLE REGISTRATION (向后兼容) ==================== + + ! Simple registration (no factory) - 保持不变 + subroutine register_component_simple(category, name) + character(len=*), intent(in) :: category, name + + type(component_info) :: info + + info%category = to_lower(trim(adjustl(category))) + info%name = to_lower(trim(adjustl(name))) + info%order = 0 + + call component_registry%register(info) + end subroutine register_component_simple + + ! Check if component exists (简单注册) + function has_component(category, name) result(found) + character(len=*), intent(in) :: category, name + logical :: found + + type(component_info) :: info + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + info = component_registry%get(cat_lower, name_lower) + found = (len_trim(info%category) > 0) + end function has_component + + ! Get available components in a category (简单注册) + subroutine get_available_components(category, names, orders) + character(len=*), intent(in) :: category + character(len=:), allocatable, intent(out), optional :: names(:) + integer, allocatable, intent(out), optional :: orders(:) + + character(len=32) :: cat_lower + integer :: i, count, idx + type(component_info) :: info + + cat_lower = to_lower(trim(adjustl(category))) + + ! Count components in this category + count = 0 + do i = 1, component_registry%simple_count + if (component_registry%simple_components(i)%category == cat_lower) then + count = count + 1 + end if + end do + + ! Allocate arrays if requested + if (present(names)) then + allocate(character(len=32) :: names(count)) + end if + + if (present(orders)) then + allocate(orders(count)) + end if + + ! Fill arrays + idx = 1 + do i = 1, component_registry%simple_count + if (component_registry%simple_components(i)%category == cat_lower) then + info = component_registry%simple_components(i) + if (present(names)) then + names(idx) = info%name + end if + if (present(orders)) then + orders(idx) = info%order + end if + idx = idx + 1 + end if + end do + end subroutine get_available_components + + ! ==================== FACTORY REGISTRATION (新增) ==================== + + ! Register component with factory function + subroutine register_component_with_factory(category, name, factory_func, order) + character(len=*), intent(in) :: category, name + procedure(factory_procedure) :: factory_func + integer, optional, intent(in) :: order + + type(component_registry_entry) :: entry + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + entry%category = cat_lower + entry%name = name_lower + + if (present(order)) then + entry%order = order + else + entry%order = 0 + end if + + entry%factory%ptr => factory_func + entry%has_factory = .true. + + call component_registry%register_with_factory(entry) + end subroutine register_component_with_factory + + ! Create component from registry using factory + function create_component_from_registry(category, name) result(instance) + character(len=*), intent(in) :: category, name + class(*), allocatable :: instance + + character(len=32) :: cat_lower, name_lower + integer :: status + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + call component_registry%create_from_factory(cat_lower, name_lower, instance, status) + + if (status /= 0) then + if (component_registry%verbose) then + print *, "[ERROR] Failed to create component: ", trim(category), ".", trim(name) + end if + end if + end function create_component_from_registry + + ! Check if component has factory + function has_factory_component(category, name) result(found) + character(len=*), intent(in) :: category, name + logical :: found + + character(len=32) :: cat_lower, name_lower + + cat_lower = to_lower(trim(adjustl(category))) + name_lower = to_lower(trim(adjustl(name))) + + found = .false. + + if (.not. component_registry%initialized) return + + ! 简化的查找逻辑(实际应调用内部方法) + found = component_registry%factory_count > 0 ! 占位实现 + end function has_factory_component + + ! List all factory components + subroutine list_factory_components(category) + character(len=*), optional, intent(in) :: category + + if (present(category)) then + call component_registry%list_with_factory(category) + else + call component_registry%list_with_factory("") + end if + end subroutine list_factory_components + + ! ==================== PUBLIC UTILITY FUNCTIONS ==================== + + ! Public function to check if registry is initialized + function registry_is_initialized() result(is_initialized) + logical :: is_initialized + is_initialized = component_registry%is_initialized() + end function registry_is_initialized + + ! Public function to get total registry size + function registry_get_size() result(size_val) + integer :: size_val + size_val = component_registry%total_size() + end function registry_get_size + + ! ==================== COMPONENT INFO METHODS ==================== + + subroutine ci_print(this) + class(component_info), intent(in) :: this + + if (this%order > 0) then + print *, " [", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")]" + else + print *, " [", trim(this%category), ".", trim(this%name), "]" + end if + end subroutine ci_print + + subroutine entry_print(this) + class(component_registry_entry), intent(in) :: this + + if (this%has_factory) then + if (this%order > 0) then + print *, " [FACTORY] ", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")" + else + print *, " [FACTORY] ", trim(this%category), ".", trim(this%name) + end if + else + if (this%order > 0) then + print *, " [SIMPLE] ", trim(this%category), ".", trim(this%name), & + " (order:", this%order, ")" + else + print *, " [SIMPLE] ", trim(this%category), ".", trim(this%name) + end if + end if + end subroutine entry_print + + logical function entry_has_factory(this) + class(component_registry_entry), intent(in) :: this + entry_has_factory = this%has_factory + end function entry_has_factory + + ! ==================== REGISTRY INTERNAL METHODS ==================== + + ! ---------- Simple Registration Methods ---------- + + subroutine cr_register(this, info) + class(component_registry_type), intent(inout) :: this + type(component_info), intent(in) :: info + + type(component_info), allocatable :: temp(:) + integer :: i + + if (.not. this%initialized) then + error stop "[ERROR] Registry not initialized, call initialize_registry first" + end if + + ! Check if already exists + do i = 1, this%simple_count + if (this%simple_components(i)%category == info%category .and. & + this%simple_components(i)%name == info%name) then + if (this%verbose) then + print *, "[WARN] Overwriting simple component: ", & + trim(info%category), ".", trim(info%name) + end if + this%simple_components(i) = info + return + end if + end do + + ! Expand array if needed + if (this%simple_count >= this%capacity) then + this%capacity = this%capacity * 2 + allocate(temp(this%capacity)) + temp(1:this%simple_count) = this%simple_components(1:this%simple_count) + call move_alloc(temp, this%simple_components) + + if (this%verbose) then + print *, "[INFO] Simple registry expanded to capacity:", this%capacity + end if + end if + + ! Add component + this%simple_count = this%simple_count + 1 + this%simple_components(this%simple_count) = info + + if (this%verbose) then + print *, "[OK] Registered simple: ", trim(info%category), ".", trim(info%name) + end if + end subroutine cr_register + + function cr_get(this, category, name) result(info) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + type(component_info) :: info + + integer :: i + + ! Initialize return value as empty + info%category = "" + info%name = "" + info%order = 0 + + if (.not. this%initialized) then + return + end if + + do i = 1, this%simple_count + if (this%simple_components(i)%category == category .and. & + this%simple_components(i)%name == name) then + info = this%simple_components(i) + return + end if + end do + end function cr_get + + subroutine cr_list_simple(this) + class(component_registry_type), intent(in) :: this + integer :: i + + if (.not. this%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + if (this%simple_count == 0) then + print *, "[INFO] No simple components registered" + return + end if + + print *, "=== Simple Registry Contents (", this%simple_count, " components) ===" + do i = 1, this%simple_count + call this%simple_components(i)%print() + end do + print *, "==========================================" + end subroutine cr_list_simple + + ! ---------- Factory Registration Methods ---------- + + subroutine cr_register_with_factory(this, entry) + class(component_registry_type), intent(inout) :: this + type(component_registry_entry), intent(in) :: entry + + type(component_registry_entry), allocatable :: temp(:) + integer :: i + + if (.not. this%initialized) then + error stop "[ERROR] Registry not initialized, call initialize_registry first" + end if + + ! Check if already exists + do i = 1, this%factory_count + if (this%factory_components(i)%category == entry%category .and. & + this%factory_components(i)%name == entry%name) then + if (this%verbose) then + print *, "[WARN] Overwriting factory component: ", & + trim(entry%category), ".", trim(entry%name) + end if + this%factory_components(i) = entry + return + end if + end do + + ! Expand array if needed + if (this%factory_count >= this%capacity) then + this%capacity = this%capacity * 2 + allocate(temp(this%capacity)) + temp(1:this%factory_count) = this%factory_components(1:this%factory_count) + call move_alloc(temp, this%factory_components) + + if (this%verbose) then + print *, "[INFO] Factory registry expanded to capacity:", this%capacity + end if + end if + + ! Add component + this%factory_count = this%factory_count + 1 + this%factory_components(this%factory_count) = entry + + if (this%verbose) then + print *, "[FACTORY] Registered with factory: ", & + trim(entry%category), ".", trim(entry%name) + end if + end subroutine cr_register_with_factory + + function cr_get_with_factory(this, category, name) result(entry) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + type(component_registry_entry) :: entry + + integer :: i + + ! Initialize return value as empty + entry%category = "" + entry%name = "" + entry%order = 0 + entry%factory%ptr => null() + entry%has_factory = .false. + + if (.not. this%initialized) then + return + end if + + do i = 1, this%factory_count + if (this%factory_components(i)%category == category .and. & + this%factory_components(i)%name == name) then + entry = this%factory_components(i) + return + end if + end do + end function cr_get_with_factory + + subroutine cr_list_with_factory(this, category) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category + + character(len=32) :: cat_lower + integer :: i, count + + if (.not. this%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + cat_lower = to_lower(trim(adjustl(category))) + + if (this%factory_count == 0) then + print *, "[INFO] No factory components registered" + return + end if + + ! Count components in this category + count = 0 + do i = 1, this%factory_count + if (len_trim(cat_lower) == 0 .or. & + this%factory_components(i)%category == cat_lower) then + count = count + 1 + end if + end do + + if (count == 0) then + if (len_trim(cat_lower) > 0) then + print *, "[INFO] No factory components in category: ", trim(cat_lower) + else + print *, "[INFO] No factory components registered" + end if + return + end if + + print *, "=== Factory Registry Contents (", count, " components) ===" + do i = 1, this%factory_count + if (len_trim(cat_lower) == 0 .or. & + this%factory_components(i)%category == cat_lower) then + call this%factory_components(i)%print_with_factory() + end if + end do + print *, "==========================================" + end subroutine cr_list_with_factory + + subroutine cr_create_from_factory(this, category, name, instance, status) + class(component_registry_type), intent(in) :: this + character(len=*), intent(in) :: category, name + class(*), allocatable, intent(out) :: instance + integer, intent(out) :: status + + type(component_registry_entry) :: entry + integer :: i + + status = -1 ! Default: not found + + if (.not. this%initialized) then + if (this%verbose) then + print *, "[ERROR] Registry not initialized" + end if + return + end if + + ! Find the entry + do i = 1, this%factory_count + if (this%factory_components(i)%category == category .and. & + this%factory_components(i)%name == name) then + entry = this%factory_components(i) + + if (entry%has_factory .and. associated(entry%factory%ptr)) then + ! Call the factory function + call entry%factory%ptr(instance) + status = 0 ! Success + + if (this%verbose) then + print *, "[FACTORY] Created component: ", & + trim(category), ".", trim(name) + end if + else + status = -2 ! No factory function + if (this%verbose) then + print *, "[ERROR] No factory function for: ", & + trim(category), ".", trim(name) + end if + end if + return + end if + end do + + ! If we get here, component not found + if (this%verbose) then + print *, "[ERROR] Factory component not found: ", & + trim(category), ".", trim(name) + end if + end subroutine cr_create_from_factory + + ! ---------- Common Methods ---------- + + subroutine cr_clear(this) + class(component_registry_type), intent(inout) :: this + + if (allocated(this%simple_components)) then + deallocate(this%simple_components) + end if + + if (allocated(this%factory_components)) then + deallocate(this%factory_components) + end if + + this%simple_count = 0 + this%factory_count = 0 + this%capacity = 100 + this%initialized = .false. + end subroutine cr_clear + + integer function cr_total_size(this) + class(component_registry_type), intent(in) :: this + cr_total_size = this%simple_count + this%factory_count + end function cr_total_size + + integer function cr_size(this) + class(component_registry_type), intent(in) :: this + cr_size = this%simple_count ! Backward compatibility + end function cr_size + + logical function cr_is_initialized(this) + class(component_registry_type), intent(in) :: this + cr_is_initialized = this%initialized + end function cr_is_initialized + + ! ==================== UTILITY FUNCTIONS ==================== + + function to_lower(str) result(lower_str) + character(len=*), intent(in) :: str + character(len=len(str)) :: lower_str + integer :: i + + do i = 1, len(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + lower_str(i:i) = char(ichar(str(i:i)) + 32) + else + lower_str(i:i) = str(i:i) + end if + end do + end function to_lower + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02c/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..46964ce7 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/src/infrastructure/CMakeLists.txt @@ -0,0 +1,20 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "配置基础设施模块...") + +add_library(infrastructure STATIC + config.f90 + mesh.f90 +) + +target_link_libraries(infrastructure PRIVATE core) + +target_include_directories(infrastructure PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS infrastructure + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "基础设施模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/02c/src/infrastructure/config.f90 new file mode 100644 index 00000000..d3b1e0df --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/src/infrastructure/config.f90 @@ -0,0 +1,90 @@ +! src/infrastructure/config.f90 +module config_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, cfd_config, config_print, config_with_reconstruction + + ! CFD configuration type + type :: cfd_config + character(len=20) :: ic_type = "step" + character(len=20) :: recon_scheme = "eno" + character(len=20) :: flux_type = "rusanov" + integer :: rk_order = 1 + real(real64) :: wave_speed = 1.0_real64 + real(real64) :: final_time = 0.625_real64 + real(real64) :: dt = 0.025_real64 + character(len=20) :: boundary_type = "periodic" + real(real64) :: left_boundary_value = 1.0_real64 + real(real64) :: right_boundary_value = 2.0_real64 + integer :: spatial_order = 2 + logical :: verbose = .true. + end type cfd_config + +contains + + subroutine config_print(this) + type(cfd_config), intent(in) :: this + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(this%ic_type) + print *, "Reconstruction: ", trim(this%recon_scheme), " (order:", this%spatial_order, ")" + print *, "Flux type: ", trim(this%flux_type) + print *, "Time integration: RK", this%rk_order + print *, "Wave speed: ", this%wave_speed + print *, "Final time: ", this%final_time + print *, "Time step: ", this%dt + print *, "Boundary: ", trim(this%boundary_type) + if (trim(this%boundary_type) == 'dirichlet') then + print *, " Dirichlet values: [", this%left_boundary_value, ", ", & + this%right_boundary_value, "]" + end if + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(this, scheme, order) + type(cfd_config), intent(inout) :: this + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + character(len=20) :: scheme_lower + + ! Convert to lowercase + scheme_lower = scheme + call to_lower_inplace(scheme_lower) + this%recon_scheme = trim(adjustl(scheme_lower)) + + ! Set order + if (present(order)) then + this%spatial_order = order + else + ! Smart defaults + if (index(this%recon_scheme, 'weno') > 0) then + this%spatial_order = 5 + else if (trim(this%recon_scheme) == 'eno') then + this%spatial_order = 3 + else + print *, "[ERROR] Unsupported reconstruction scheme: ", trim(this%recon_scheme) + return + end if + end if + + if (this%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(this%recon_scheme), & + " Order: ", this%spatial_order + end if + end subroutine config_with_reconstruction + + subroutine to_lower_inplace(str) + character(len=*), intent(inout) :: str + integer :: i + + do i = 1, len_trim(str) + if (str(i:i) >= 'A' .and. str(i:i) <= 'Z') then + str(i:i) = char(ichar(str(i:i)) + 32) + end if + end do + end subroutine to_lower_inplace + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/02c/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..896969bd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/src/infrastructure/mesh.f90 @@ -0,0 +1,74 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, mesh_type, mesh_init, mesh_print_info + + ! mesh + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer :: ncells = 40 + integer :: nnodes + integer :: nx + real(wp), allocatable :: x(:) ! + real(wp), allocatable :: xcc(:) ! + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer, optional, intent(in) :: ncells + + integer :: i + + ! Set + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! computation + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! node + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! cell + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== ===" + print *, ": [", this%xmin, ", ", this%xmax, "]" + print *, ": ", this%ncells + print *, ": ", this%nnodes + print *, " dx: ", this%dx + print *, " L: ", this%L + print *, "==========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/src/manager/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02c/src/manager/CMakeLists.txt new file mode 100644 index 00000000..00c8bf49 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/src/manager/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/manager/CMakeLists.txt +message(STATUS "配置管理器模块...") + +# 创建管理器库 +add_library(manager STATIC + component_manager.f90 + component_factory.f90 +) + +# 明确依赖关系:管理器依赖所有其他模块 +target_link_libraries(manager + PRIVATE + core + infrastructure + reconstructor + flux +) + +# 设置模块输出目录 +set_target_properties(manager PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "管理器模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/src/manager/component_factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/02c/src/manager/component_factory.f90 new file mode 100644 index 00000000..114fedea --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/src/manager/component_factory.f90 @@ -0,0 +1,127 @@ +! src/manager/component_factory.f90 +module component_factory_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + use eno_reconstructor_module, only: eno_reconstructor, create_eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor, create_weno3_reconstructor + use rusanov_flux_module, only: rusanov_flux, create_rusanov_flux + + implicit none + private + public :: wp, create_reconstructor, create_flux_calculator + + ! 错误代码 + integer, parameter :: CM_SUCCESS = 0 + integer, parameter :: CM_ERROR_UNKNOWN_SCHEME = 1 + integer, parameter :: CM_ERROR_UNKNOWN_FLUX = 2 + integer, parameter :: CM_ERROR_INVALID_ORDER = 3 + +contains + + ! ==================== 重构器创建 ==================== + + function create_reconstructor(config, status) result(recon) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(reconstructor_base), allocatable :: recon + + character(len=20) :: scheme + integer :: order, error_code + + scheme = trim(adjustl(config%recon_scheme)) + order = config%spatial_order + + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating reconstructor: ", scheme, " order=", order + end if + + select case(scheme) + case('eno') + allocate(eno_reconstructor :: recon) + select type(recon) + type is(eno_reconstructor) + recon = create_eno_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case('weno3') + allocate(weno3_reconstructor :: recon) + select type(recon) + type is(weno3_reconstructor) + recon = create_weno3_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case default + error_code = CM_ERROR_UNKNOWN_SCHEME + if (config%verbose) then + print *, "[ERROR] Unknown reconstructor scheme: ", scheme + print *, " Available: eno, weno3" + end if + end select + + ! 检查阶数有效性 + if (error_code == CM_SUCCESS) then + if (order < 1) then + error_code = CM_ERROR_INVALID_ORDER + if (config%verbose) then + print *, "[ERROR] Invalid spatial order: ", order + end if + end if + end if + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Reconstructor creation failed" + end if + end function create_reconstructor + + ! ==================== 通量计算器创建 ==================== + + function create_flux_calculator(config, status) result(flux) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(flux_calculator_base), allocatable :: flux + + character(len=20) :: flux_type + integer :: error_code + + flux_type = trim(adjustl(config%flux_type)) + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating flux calculator: ", flux_type + end if + + select case(flux_type) + case('rusanov') + allocate(rusanov_flux :: flux) + select type(flux) + type is(rusanov_flux) + flux = create_rusanov_flux() + flux%wave_speed_default = config%wave_speed + end select + + case default + error_code = CM_ERROR_UNKNOWN_FLUX + if (config%verbose) then + print *, "[ERROR] Unknown flux type: ", flux_type + print *, " Available: rusanov" + end if + end select + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Flux calculator creation failed" + end if + end function create_flux_calculator + +end module component_factory_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/src/manager/component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/02c/src/manager/component_manager.f90 new file mode 100644 index 00000000..9e095c25 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/src/manager/component_manager.f90 @@ -0,0 +1,75 @@ +! src/manager/component_manager.f90 +module component_manager_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use component_factory_module, only: create_reconstructor, create_flux_calculator + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + implicit none + private + public :: wp, component_manager_info, validate_config + public :: create_reconstructor, create_flux_calculator + +contains + + ! ==================== 配置验证 ==================== + + function validate_config(config) result(is_valid) + type(cfd_config), intent(in) :: config + logical :: is_valid + + integer :: status + class(reconstructor_base), allocatable :: test_recon + class(flux_calculator_base), allocatable :: test_flux + + is_valid = .false. + + ! 测试创建重构器 + test_recon = create_reconstructor(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid reconstructor configuration" + end if + return + end if + + ! 测试创建通量计算器 + test_flux = create_flux_calculator(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid flux configuration" + end if + return + end if + + ! 清理测试组件 + if (allocated(test_recon)) deallocate(test_recon) + if (allocated(test_flux)) deallocate(test_flux) + + is_valid = .true. + + if (config%verbose) then + print *, "[CONFIG VALIDATION] Configuration is valid" + end if + end function validate_config + + ! ==================== 信息显示 ==================== + + subroutine component_manager_info() + print *, "=== Component Manager ===" + print *, "Available reconstructors:" + print *, " - eno (orders: 1-7)" + print *, " - weno3 (order: 3)" + print *, "" + print *, "Available flux calculators:" + print *, " - rusanov" + print *, "" + print *, "Features:" + print *, " - Configuration validation" + print *, " - Component creation from config" + print *, " - Error handling with status codes" + print *, "=========================" + end subroutine component_manager_info + +end module component_manager_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02c/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02c/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..3fde7746 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,28 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux STATIC + base.f90 + rusanov.f90 +) + +#target_link_libraries(flux PRIVATE core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 明确设置不使用 /Qm64 选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + set_target_properties(flux PROPERTIES + Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /Qm64" # 明确设置,避免继承问题 + ) +endif() + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/02c/src/numerics/flux/base.f90 new file mode 100644 index 00000000..7080a7ae --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/src/numerics/flux/base.f90 @@ -0,0 +1,30 @@ +!src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + procedure :: print_basic_info => flux_print_basic ! 添加辅助方法 + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + + subroutine flux_print_basic(this) + class(flux_calculator_base), intent(in) :: this + print *, " Name: ", trim(this%name) + end subroutine flux_print_basic + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/02c/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..daa9e3bb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/src/numerics/flux/rusanov.f90 @@ -0,0 +1,41 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux, create_rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + + ! 构造函数接口 + interface rusanov_flux + module procedure create_rusanov_flux + end interface + +contains + + ! 构造函数 + type(rusanov_flux) function create_rusanov_flux() result(this) + this%name = "Rusanov" + this%wave_speed_default = 1.0_real64 + end function create_rusanov_flux + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Flux calculator information:" + call this%print_basic_info() + + ! 添加Rusanov特有信息 + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02c/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..5e4b938d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,22 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor STATIC + base.f90 + eno.f90 + weno3.f90 +) + +target_link_libraries(reconstructor PRIVATE core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/02c/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..53798d02 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/src/numerics/reconstructor/base.f90 @@ -0,0 +1,33 @@ +!src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + procedure :: print_basic_info => reconstructor_print_basic ! 添加一个辅助方法 + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + + subroutine reconstructor_print_basic(this) + class(reconstructor_base), intent(in) :: this + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_print_basic + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/02c/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..f973e8b3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor, create_eno_reconstructor ! ← 添加这个 + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + + ! 构造函数接口 + interface eno_reconstructor + module procedure create_eno_reconstructor + end interface + +contains + + ! 构造函数 + type(eno_reconstructor) function create_eno_reconstructor() result(this) + this%name = "ENO" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_eno_reconstructor + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加ENO特有信息 + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/02c/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..d5b7a747 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno3_reconstructor, create_weno3_reconstructor + + type, extends(reconstructor_base) :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + + ! 构造函数接口 + interface weno3_reconstructor + module procedure create_weno3_reconstructor + end interface + +contains + + ! 构造函数 + type(weno3_reconstructor) function create_weno3_reconstructor() result(this) + this%name = "WENO3" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_weno3_reconstructor + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO3特有信息 + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02c/tests/CMakeLists.txt new file mode 100644 index 00000000..1c7c685b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/tests/CMakeLists.txt @@ -0,0 +1,50 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +add_executable(test_minimal_simple test_minimal_simple.f90) + +message(STATUS "CMAKE_Fortran_MODULE_DIRECTORY=${CMAKE_Fortran_MODULE_DIRECTORY}") + +#target_include_directories( test_minimal_simple +# PRIVATE +# ${CMAKE_Fortran_MODULE_DIRECTORY} +#) + +target_link_libraries( test_minimal_simple + PRIVATE + infrastructure +) + +add_executable(test_simple_link test_simple_link.f90) +target_link_libraries(test_simple_link + PRIVATE + reconstructor + flux +) + + +add_executable(test_factory_simple test_factory_simple.f90) +target_link_libraries(test_factory_simple + PRIVATE + core + infrastructure + reconstructor + flux +) + +# 更新测试链接 +add_executable(test_component_manager test_component_manager.f90) + +target_link_libraries(test_component_manager + PRIVATE + manager # ← 链接到新的管理器库 + infrastructure +) + +add_executable(test_architecture test_architecture.f90) +target_link_libraries(test_architecture + PRIVATE + core + infrastructure + manager +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/tests/test_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/02c/tests/test_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/tests/test_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/tests/test_component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/02c/tests/test_component_manager.f90 new file mode 100644 index 00000000..f60c3505 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/tests/test_component_manager.f90 @@ -0,0 +1,111 @@ +! tests/test_component_manager.f90 +program test_component_manager + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use component_manager_module, only: create_reconstructor, create_flux_calculator + use component_manager_module, only: component_manager_info, validate_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + implicit none + + type(cfd_config) :: config + class(reconstructor_base), allocatable :: recon + class(flux_calculator_base), allocatable :: flux + integer :: status + logical :: is_valid + + print *, "=== Component Manager Test ===" + print *, "" + + ! 显示组件管理器信息 + call component_manager_info() + print *, "" + + ! 测试1: 基本配置 + print *, "1. Testing basic ENO3 + Rusanov configuration..." + print *, "-----------------------------------------------" + + config%verbose = .true. + call config_print(config) + + ! 配置ENO3重构 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + call config_print(config) + print *, "" + + ! 验证配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Configuration is valid" + else + print *, "[ERROR] Configuration is invalid" + end if + print *, "" + + ! 测试2: 创建组件 + print *, "2. Testing component creation..." + print *, "--------------------------------" + + ! 创建重构器(带状态检查) + recon = create_reconstructor(config, status) + if (status == 0) then + print *, "[OK] Reconstructor created successfully" + call recon%info() + else + print *, "[ERROR] Failed to create reconstructor, code:", status + end if + print *, "" + + ! 创建通量计算器 + flux = create_flux_calculator(config, status) + if (status == 0) then + print *, "[OK] Flux calculator created successfully" + call flux%info() + else + print *, "[ERROR] Failed to create flux calculator, code:", status + end if + print *, "" + + ! 测试3: WENO3重构测试 + print *, "3. Testing WENO3 configuration..." + print *, "---------------------------------" + + call config_with_reconstruction(config, "weno3", 3) + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] WENO3 configuration is valid" + + ! 创建WENO3重构器 + recon = create_reconstructor(config) + call recon%info() + else + print *, "[ERROR] WENO3 configuration is invalid" + end if + print *, "" + + ! 测试4: 错误配置测试 + print *, "4. Testing invalid configuration..." + print *, "-----------------------------------" + + config%recon_scheme = "unknown_scheme" + config%flux_type = "unknown_flux" + + is_valid = validate_config(config) + if (.not. is_valid) then + print *, "[OK] Invalid configuration correctly rejected" + else + print *, "[ERROR] Invalid configuration should have been rejected" + end if + + ! 清理 + if (allocated(recon)) deallocate(recon) + if (allocated(flux)) deallocate(flux) + + print *, "" + print *, "=== Component manager test completed successfully ===" + +end program test_component_manager \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/tests/test_factory_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/02c/tests/test_factory_simple.f90 new file mode 100644 index 00000000..d4139bb1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/tests/test_factory_simple.f90 @@ -0,0 +1,86 @@ +!tests/test_factory_simple.f90 +program test_factory_simple + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module, only: initialize_registry, cleanup_registry, & + register_component_simple, has_component + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Basic systems + print *, "1. Testing basic systems..." + print *, "-----------------------------" + call config_print(config) + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Creating reconstructors + print *, "2. Testing reconstructors..." + print *, "------------------------------" + + ! 创建并测试ENO重构器 + print *, "Creating ENO reconstructor..." + eno = eno_reconstructor() ! 使用构造函数 + call eno%info() ! 必须调用info方法 + + print *, "" + print *, "Creating WENO3 reconstructor..." + weno3 = weno3_reconstructor() ! 使用构造函数 + call weno3%info() ! 必须调用info方法 + print *, "" + + ! Test 3: Creating flux calculator + print *, "3. Testing flux calculator..." + print *, "-------------------------------" + + print *, "Creating Rusanov flux calculator..." + rusanov = rusanov_flux() ! 使用构造函数 + call rusanov%info() ! 必须调用info方法 + print *, "" + + ! Test 4: Registry integration + print *, "4. Testing registry..." + print *, "----------------------" + + call initialize_registry(verbose=.true.) + + ! Register components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! Check registration + if (has_component("reconstructor", "eno")) then + print *, "[OK] ENO reconstructor registered successfully" + end if + + if (has_component("reconstructor", "weno3")) then + print *, "[OK] WENO3 reconstructor registered successfully" + end if + + if (has_component("flux", "rusanov")) then + print *, "[OK] Rusanov flux registered successfully" + end if + + print *, "" + call cleanup_registry() + + print *, "=== Factory pattern simple test completed successfully ===" + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/tests/test_minimal_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/02c/tests/test_minimal_simple.f90 new file mode 100644 index 00000000..cc6753b5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/tests/test_minimal_simple.f90 @@ -0,0 +1,84 @@ +! tests/test_minimal_simple.f90 +program test_minimal_simple + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Simple Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_simple() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components (simple version) + print *, "5. Testing registry functionality" + print *, "---------------------------------" + print *, "Registry initialized: ", registry_is_initialized() + print *, "Number of components: ", registry_get_size() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Simple test completed successfully ===" + +end program test_minimal_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02c/tests/test_simple_link.f90 b/example/1d-linear-convection/weno3/fortran/registry/02c/tests/test_simple_link.f90 new file mode 100644 index 00000000..27b165b0 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02c/tests/test_simple_link.f90 @@ -0,0 +1,108 @@ +! tests/test_minimal.f90 +program test_minimal + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i ! Declare loop variable here + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_simple() + print *, "Registry size: ", component_registry%size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components + print *, "5. Testing available components" + print *, "--------------------------------" + + ! Use a separate block to avoid allocation issues + call test_available_components() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Minimal test completed successfully ===" + +contains + + ! Internal subroutine for testing available components + subroutine test_available_components() + character(len=:), allocatable :: names(:) + integer, allocatable :: orders(:) + integer :: j + + call get_available_components("reconstructor", names, orders) + + if (allocated(names)) then + print *, "Reconstructors:" + do j = 1, size(names) + print *, " - ", trim(names(j)) + if (allocated(orders)) then + print *, " Order: ", orders(j) + end if + end do + else + print *, "No reconstructors found" + end if + end subroutine test_available_components + +end program test_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02d/CMakeLists.txt new file mode 100644 index 00000000..ef66d584 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/README.md b/example/1d-linear-convection/weno3/fortran/registry/02d/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/02d/scripts/build.bat new file mode 100644 index 00000000..6fd6dc03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/scripts/build.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Project Builder +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python build script with full Intel environment support... +echo. + +python build.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Build failed + pause + exit /b 1 +) + +echo. +echo [INFO] Build completed successfully! +echo. +echo [INFO] To run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/02d/scripts/build.py new file mode 100644 index 00000000..3bf6d537 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/scripts/build.py @@ -0,0 +1,629 @@ +#!/usr/bin/env python3 +""" +Fortran CFD Project Builder - 完整Python解决方案 +在Python内部处理Intel oneAPI环境配置 +""" + +import os +import sys +import subprocess +import shutil +import argparse +import time +import platform +import tempfile +from pathlib import Path + +class IntelEnvironment: + """Intel oneAPI环境管理器""" + + def __init__(self): + self.setvars_path = None + self.env_vars = {} + + def find_setvars(self): + """查找setvars.bat文件""" + possible_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + r"C:\Program Files (x86)\Intel\oneAPI\compiler\latest\env\vars.bat", + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\setvars.bat"), + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\compiler\latest\env\vars.bat"), + ] + + for path in possible_paths: + if os.path.exists(path): + self.setvars_path = path + return True + + return False + + def setup_environment(self): + """设置Intel环境""" + if not self.find_setvars(): + return False + + try: + # 创建临时的批处理文件来捕获环境变量 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + f.write(f'@echo off\n') + f.write(f'call "{self.setvars_path}" >nul 2>&1\n') + f.write(f'set\n') # 输出所有环境变量 + temp_bat = f.name + + # 运行批处理文件并捕获输出 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + shell=True + ) + + # 解析环境变量 + for line in result.stdout.split('\n'): + line = line.strip() + if '=' in line: + key, value = line.split('=', 1) + self.env_vars[key.strip()] = value.strip() + + # 清理临时文件 + os.unlink(temp_bat) + + # 更新当前进程的环境变量 + os.environ.update(self.env_vars) + + return True + + except Exception as e: + print(f"设置Intel环境失败: {e}") + return False + + def get_compiler_info(self): + """获取编译器信息""" + info = {} + + # 检查ifx编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + env={**os.environ, **self.env_vars} if self.env_vars else os.environ + ) + + if result.returncode == 0: + for line in result.stdout.split('\n'): + if 'Version' in line or '版本' in line: + info['ifx_version'] = line.strip() + break + except: + pass + + # 检查环境变量 + info['ifx_root'] = self.env_vars.get('IFX_ROOT', '') + info['compiler_root'] = self.env_vars.get('ONEAPI_ROOT', '') + + return info + +class BuildSystem: + """构建系统主类""" + + def __init__(self): + self.project_root = Path(__file__).parent.parent + self.build_dir = self.project_root / "build" + self.intel_env = IntelEnvironment() + + # 设置控制台编码 + if sys.platform == "win32": + try: + import ctypes + # 设置控制台输出为UTF-8 + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + except: + pass + + def print_header(self, text): + """打印标题""" + print(f"\n{'='*70}") + print(f" {text}") + print(f"{'='*70}\n") + + def print_step(self, step, total, message): + """打印步骤""" + print(f"[{step}/{total}] {message}...") + + def print_success(self, message): + """打印成功""" + print(f"\033[92m✓ {message}\033[0m") + + def print_error(self, message): + """打印错误""" + print(f"\033[91m✗ {message}\033[0m") + + def print_warning(self, message): + """打印警告""" + print(f"\033[93m! {message}\033[0m") + + def print_info(self, message): + """打印信息""" + print(f"\033[94mℹ {message}\033[0m") + + def check_prerequisites(self): + """检查前提条件""" + self.print_step(1, 6, "检查前提条件") + + # 检查Python版本 + python_version = sys.version.split()[0] + self.print_info(f"Python版本: {python_version}") + + # 检查平台 + self.print_info(f"平台: {platform.system()} {platform.release()}") + self.print_info(f"处理器核心数: {os.cpu_count()}") + + # 检查CMake + try: + result = subprocess.run( + ["cmake", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + version_line = result.stdout.split('\n')[0] + self.print_success(f"CMake: {version_line}") + else: + self.print_error("CMake未找到") + return False + except FileNotFoundError: + self.print_error("CMake未安装") + return False + + return True + + def setup_intel_environment(self, args): + """设置Intel环境""" + self.print_step(2, 6, "配置Intel oneAPI环境") + + if not self.intel_env.find_setvars(): + self.print_warning("未找到Intel oneAPI setvars.bat") + self.print_info("将尝试使用系统环境中的编译器") + + # 检查是否能直接访问编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + self.print_success("Intel编译器在系统PATH中找到") + return True + else: + self.print_warning("Intel编译器未在PATH中找到") + except: + self.print_warning("无法访问Intel编译器") + + return True # 继续,让CMake自己找编译器 + + # 设置环境 + if self.intel_env.setup_environment(): + compiler_info = self.intel_env.get_compiler_info() + + if compiler_info.get('ifx_version'): + self.print_success(f"Intel Fortran编译器: {compiler_info['ifx_version']}") + elif compiler_info.get('ifx_root'): + self.print_success(f"Intel编译器路径: {compiler_info['ifx_root']}") + else: + self.print_success("Intel oneAPI环境配置完成") + + return True + else: + self.print_warning("Intel环境配置失败,将继续使用系统环境") + return True + + def clean_build_directory(self, args): + """清理构建目录""" + if args.clean and self.build_dir.exists(): + self.print_info("清理构建目录...") + try: + shutil.rmtree(self.build_dir) + self.print_success("构建目录已清理") + except Exception as e: + self.print_error(f"清理失败: {e}") + if not args.force: + return False + return True + + def run_command(self, cmd, cwd=None, check=True, env=None): + """运行命令""" + if isinstance(cmd, list): + cmd_str = ' '.join(str(c) for c in cmd if c) + else: + cmd_str = str(cmd) + + print(f" \033[96m$\033[0m {cmd_str}") + + try: + # 合并环境变量 + exec_env = os.environ.copy() + if env: + exec_env.update(env) + if self.intel_env.env_vars: + exec_env.update(self.intel_env.env_vars) + + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=False, + env=exec_env + ) + + # 处理输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or '完成' in line or '生成' in line: + print(f" \033[92m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + if check and result.returncode != 0: + self.print_error(f"命令执行失败,退出码: {result.returncode}") + return False + + return True + + except Exception as e: + self.print_error(f"命令执行异常: {e}") + return False + + def configure_cmake(self, args): + """配置CMake""" + self.print_step(3, 6, "配置CMake项目") + + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ] + + if args.compiler == "ifx": + cmake_cmd.extend(["-T", "fortran=ifx"]) + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + success = self.run_command(cmake_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("CMake配置完成") + else: + self.print_error("CMake配置失败") + + return success + + def build_project(self, args): + """构建项目""" + self.print_step(4, 6, "构建项目") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + success = self.run_command(build_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("项目构建完成") + else: + self.print_error("构建失败") + + return success + + def run_tests_with_environment(self, test_exe): + """运行单个测试,确保有Intel环境""" + try: + # 创建临时的批处理文件来运行测试 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'"{test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'"{test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + return result + + except Exception as e: + print(f"运行测试失败: {e}") + return None + + def run_tests(self, args): + """运行测试""" + self.print_step(5, 6, "运行测试") + + # 查找测试可执行文件 + test_dir = self.build_dir / "bin" / args.build_type + if not test_dir.exists(): + test_dir = self.build_dir / "bin" + if not test_dir.exists(): + test_dir = self.build_dir + + test_files = list(test_dir.glob("test_*.exe")) + + if not test_files: + self.print_warning("未找到测试程序") + return True + + all_passed = True + + for test_exe in sorted(test_files): + test_name = test_exe.stem + self.print_info(f"运行测试: {test_name}") + print(f" {'-'*50}") + + # 运行测试 + result = self.run_tests_with_environment(str(test_exe)) + + if result is None: + self.print_error(f" {test_name} 运行失败") + all_passed = False + continue + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + print(f" {line}") + + if result.returncode == 0: + self.print_success(f" {test_name} 通过") + else: + self.print_error(f" {test_name} 失败 (退出码: {result.returncode})") + all_passed = False + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + print() # 空行 + + return all_passed + + def create_test_runner(self, args): + """创建独立的测试运行器""" + self.print_step(6, 6, "创建测试运行器") + + runner_path = self.build_dir / "run_tests.bat" + + content = f'''@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Test Runner +echo ======================================== +echo. + +REM Setup Intel oneAPI environment +set "SETVARS_PATH={self.intel_env.setvars_path or ''}" +if exist "%SETVARS_PATH%" ( + call "%SETVARS_PATH%" >nul + echo [INFO] Intel environment configured +) else ( + echo [WARNING] Intel environment not found + echo [WARNING] Tests may fail without runtime libraries +) + +echo. + +REM Run all test executables +set "TEST_COUNT=0" +set "PASS_COUNT=0" + +for %%f in ("bin\\{args.build_type}\\test_*.exe") do ( + set /a TEST_COUNT+=1 + echo [TEST %%f] + echo {'-'*50} + + %%f + if errorlevel 1 ( + echo [FAILED] %%f + ) else ( + echo [PASSED] %%f + set /a PASS_COUNT+=1 + ) + echo. +) + +echo ======================================== +echo Tests: %PASS_COUNT%/%TEST_COUNT% passed +if %PASS_COUNT% equ %TEST_COUNT% ( + echo [SUCCESS] All tests passed! +) else ( + echo [FAILURE] Some tests failed +) +echo ======================================== + +pause +''' + + with open(runner_path, 'w', encoding='utf-8') as f: + f.write(content) + + self.print_success(f"测试运行器已创建: {runner_path}") + self.print_info(f"使用方法: cd build && run_tests.bat") + + return runner_path + + def generate_report(self, args, build_time, tests_passed): + """生成构建报告""" + self.print_header("构建完成") + + print(f"项目: {self.project_root.name}") + print(f"构建类型: {args.build_type}") + print(f"编译器: {args.compiler}") + print(f"并行作业: {args.jobs}") + print(f"总耗时: {build_time:.1f}秒") + print(f"测试结果: {'全部通过' if tests_passed else '有失败'}") + + # 显示生成的可执行文件 + bin_dir = self.build_dir / "bin" / args.build_type + if bin_dir.exists(): + print(f"\n生成的可执行文件:") + for exe in sorted(bin_dir.glob("*.exe")): + size_mb = exe.stat().st_size / (1024 * 1024) + print(f" • {exe.name} ({size_mb:.2f} MB)") + + # 显示测试运行器信息 + runner_path = self.build_dir / "run_tests.bat" + if runner_path.exists(): + print(f"\n独立测试运行器:") + print(f" • {runner_path.name}") + print(f" 在Intel oneAPI环境中运行所有测试") + + def run(self): + """运行构建系统""" + parser = argparse.ArgumentParser( + description="Fortran CFD项目构建工具", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认构建 + %(prog)s --clean # 清理后构建 + %(prog)s --build-type Release # Release构建 + %(prog)s --no-tests # 只构建,不运行测试 + %(prog)s -j8 --verbose # 8线程并行构建,详细输出 + """ + ) + + parser.add_argument("--build-type", choices=["Debug", "Release"], + default="Debug", help="构建类型") + parser.add_argument("--compiler", choices=["ifx", "ifort"], + default="ifx", help="Fortran编译器") + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-tests", action="store_true", + help="跳过测试") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + + args = parser.parse_args() + + # 开始构建 + start_time = time.time() + + self.print_header("Fortran CFD 项目构建系统") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 检查前提条件 + if not self.check_prerequisites(): + if not args.force: + return 1 + + # 2. 设置Intel环境 + if not self.setup_intel_environment(args): + if not args.force: + return 1 + + # 3. 清理目录 + if not self.clean_build_directory(args): + if not args.force: + return 1 + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 4. 配置CMake + if not self.configure_cmake(args): + if not args.force: + return 1 + + # 5. 构建项目 + if not self.build_project(args): + if not args.force: + return 1 + + # 6. 运行测试和创建测试运行器 + tests_passed = True + if not args.no_tests: + tests_passed = self.run_tests(args) + + # 创建测试运行器 + self.create_test_runner(args) # 传递 args 参数 + + # 7. 生成报告 + build_time = time.time() - start_time + self.generate_report(args, build_time, tests_passed) + + return 0 if tests_passed else 1 + + except KeyboardInterrupt: + self.print_error("\n构建被用户中断") + return 1 + except Exception as e: + self.print_error(f"构建过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + builder = BuildSystem() + return builder.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/02d/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02d/src/CMakeLists.txt new file mode 100644 index 00000000..d8cbd5f3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/src/CMakeLists.txt @@ -0,0 +1,16 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(base) +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) +add_subdirectory(manager) + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/src/base/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02d/src/base/CMakeLists.txt new file mode 100644 index 00000000..d537affd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/src/base/CMakeLists.txt @@ -0,0 +1,15 @@ +# src/base/CMakeLists.txt +message(STATUS "Configuring base module...") + +add_library(base STATIC + modules.f90 +) + +set_target_properties(base PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Base module configured") + +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/src/base/modules.f90 b/example/1d-linear-convection/weno3/fortran/registry/02d/src/base/modules.f90 new file mode 100644 index 00000000..99ef3337 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/src/base/modules.f90 @@ -0,0 +1,42 @@ +! src/base/modules.f90 +module base_modules + use, intrinsic :: iso_fortran_env, only: real64, int32 + + implicit none + + ! 公开所有基本类型 + public :: wp, ip, string_len, max_name_len + public :: cfd_config_base, component_info + + ! 工作精度类型 + integer, parameter :: wp = real64 + integer, parameter :: ip = int32 + + ! 字符串长度常量(使用数值常量) + integer, parameter :: string_len = 100 + integer, parameter :: max_name_len = 32 + + ! 基础配置类型 + type :: cfd_config_base + character(len=max_name_len) :: ic_type = "step" + character(len=max_name_len) :: recon_scheme = "eno" + character(len=max_name_len) :: flux_type = "rusanov" + integer(ip) :: rk_order = 1 + real(wp) :: wave_speed = 1.0_wp + real(wp) :: final_time = 0.625_wp + real(wp) :: dt = 0.025_wp + character(len=max_name_len) :: boundary_type = "periodic" + integer(ip) :: spatial_order = 2 + character(len=max_name_len) :: equation_type = "linear_advection" + character(len=max_name_len) :: problem_type = "linear_advection" + logical :: verbose = .true. + end type cfd_config_base + + ! 组件信息类型 + type :: component_info + character(len=max_name_len) :: category = "" + character(len=max_name_len) :: name = "" + integer(ip) :: order = 0 + end type component_info + +end module base_modules \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02d/src/core/CMakeLists.txt new file mode 100644 index 00000000..d8b8df06 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/src/core/CMakeLists.txt @@ -0,0 +1,14 @@ +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") + +add_library(core STATIC + registry.f90 +) + +target_link_libraries(core PRIVATE base) + +set_target_properties(core PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Core module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/src/core/factory_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/02d/src/core/factory_base.f90 new file mode 100644 index 00000000..302418a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/src/core/factory_base.f90 @@ -0,0 +1,57 @@ +! src/core/factory_base.f90 +module factory_base_module + use base_modules, only: wp, ip + use registry_module, only: create_component, has_component + + implicit none + private + public :: wp, ip, factory_base, factory_create + + ! 工厂基类 + type :: factory_base + character(len=max_name_length) :: category = "" + contains + procedure :: create => factory_base_create + procedure :: get_available => factory_base_get_available + end type factory_base + + ! 便捷函数类型 + abstract interface + function factory_function_interface(category, name) result(instance) + import :: wp + character(len=*), intent(in) :: category, name + class(*), allocatable :: instance + end function factory_function_interface + end interface + +contains + + ! 创建工厂实例 + function factory_create(category) result(factory) + character(len=*), intent(in) :: category + type(factory_base) :: factory + factory%category = trim(category) + end function factory_create + + ! 工厂创建方法 + function factory_base_create(this, name) result(instance) + class(factory_base), intent(in) :: this + character(len=*), intent(in) :: name + class(*), allocatable :: instance + + instance = create_component(this%category, name) + end function factory_base_create + + ! 获取可用组件列表(简化版) + subroutine factory_base_get_available(this, names, count) + class(factory_base), intent(in) :: this + character(len=*), allocatable, intent(out) :: names(:) + integer(ip), intent(out) :: count + + ! 这里需要实现从注册表获取列表的逻辑 + ! 暂时返回空列表 + count = 0 + allocate(character(len=max_name_length) :: names(0)) + end subroutine factory_base_get_available + +end module factory_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/02d/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..a960167c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/02d/src/core/registry.f90 new file mode 100644 index 00000000..acc63edb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/src/core/registry.f90 @@ -0,0 +1,203 @@ +! src/core/registry.f90 +module registry_module + use base_modules, only: wp, ip, max_name_len, component_info + + implicit none + private + + ! 明确公开所有需要的接口 + public :: wp, ip ! 类型参数 + public :: component_info ! 类型 + public :: registry_init, registry_cleanup ! 初始化/清理 + public :: register_component_simple ! 注册组件 + public :: has_component_simple ! 检查组件 + public :: list_components ! 列出组件 + public :: registry_is_initialized ! 检查初始化状态 ← 新增 + public :: registry_get_size ! 获取大小 ← 新增 + + ! 全局注册表 + type :: component_registry + type(component_info), allocatable :: components(:) + integer(ip) :: count = 0 + integer(ip) :: capacity = 100 + logical :: initialized = .false. + logical :: verbose = .true. + end type component_registry + + type(component_registry) :: registry + +contains + + ! ==================== 公共API ==================== + + subroutine registry_init(verbose) + logical, optional, intent(in) :: verbose + + if (registry%initialized) then + if (registry%verbose) then + print *, "[REGISTRY] Already initialized" + end if + return + end if + + if (present(verbose)) then + registry%verbose = verbose + end if + + allocate(registry%components(registry%capacity)) + registry%initialized = .true. + + if (registry%verbose) then + print *, "[REGISTRY] Initialized with capacity:", registry%capacity + end if + end subroutine registry_init + + subroutine registry_cleanup() + if (allocated(registry%components)) then + deallocate(registry%components) + end if + registry%initialized = .false. + registry%count = 0 + + if (registry%verbose) then + print *, "[REGISTRY] Cleaned up" + end if + end subroutine registry_cleanup + + subroutine register_component_simple(category, name, order) + character(len=*), intent(in) :: category, name + integer(ip), optional, intent(in) :: order + + integer(ip) :: i + type(component_info) :: info + + if (.not. registry%initialized) then + call registry_init() + end if + + ! 检查是否已存在 + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + if (registry%verbose) then + print *, "[WARN] Overwriting component: ", trim(category), ".", trim(name) + end if + + ! 更新 + if (present(order)) then + registry%components(i)%order = order + else + registry%components(i)%order = 0 + end if + return + end if + end do + + ! 扩展数组 + if (registry%count >= registry%capacity) then + call expand_registry() + end if + + ! 添加新组件 + registry%count = registry%count + 1 + + info%category = trim(category) + info%name = trim(name) + info%order = 0 + if (present(order)) then + info%order = order + end if + + registry%components(registry%count) = info + + if (registry%verbose) then + print *, "[OK] Registered simple: ", trim(category), ".", trim(name) + end if + end subroutine register_component_simple + + logical function has_component_simple(category, name) + character(len=*), intent(in) :: category, name + + integer(ip) :: i + + has_component_simple = .false. + + if (.not. registry%initialized) return + + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + has_component_simple = .true. + return + end if + end do + end function has_component_simple + + subroutine list_components(category) + character(len=*), optional, intent(in) :: category + + integer(ip) :: i, count + + if (.not. registry%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + if (registry%count == 0) then + print *, "[INFO] No components registered" + return + end if + + count = 0 + print *, "=== Registry Contents ===" + do i = 1, registry%count + if (.not. present(category) .or. & + trim(registry%components(i)%category) == trim(category)) then + call print_component_info(registry%components(i)) + count = count + 1 + end if + end do + + print *, "Total:", count, "components" + print *, "==========================" + end subroutine list_components + + ! ==================== 新增函数 ==================== + + logical function registry_is_initialized() + ! 检查注册表是否已初始化 + registry_is_initialized = registry%initialized + end function registry_is_initialized + + integer(ip) function registry_get_size() + ! 获取注册表中的组件数量 + registry_get_size = registry%count + end function registry_get_size + + ! ==================== 内部辅助函数 ==================== + + subroutine expand_registry() + type(component_info), allocatable :: temp(:) + + registry%capacity = registry%capacity * 2 + allocate(temp(registry%capacity)) + temp(1:registry%count) = registry%components(1:registry%count) + call move_alloc(temp, registry%components) + + if (registry%verbose) then + print *, "[INFO] Registry expanded to capacity:", registry%capacity + end if + end subroutine expand_registry + + subroutine print_component_info(info) + type(component_info), intent(in) :: info + + if (info%order > 0) then + print *, " [", trim(info%category), ".", trim(info%name), & + " (order:", info%order, ")]" + else + print *, " [", trim(info%category), ".", trim(info%name), "]" + end if + end subroutine print_component_info + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02d/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..c48396af --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/src/infrastructure/CMakeLists.txt @@ -0,0 +1,15 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "Configuring infrastructure module...") + +add_library(infrastructure STATIC + config.f90 + mesh.f90 +) + +target_link_libraries(infrastructure PRIVATE base) + +set_target_properties(infrastructure PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Infrastructure module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/02d/src/infrastructure/config.f90 new file mode 100644 index 00000000..719e14d2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/src/infrastructure/config.f90 @@ -0,0 +1,64 @@ +! src/infrastructure/config.f90 +module config_module + use base_modules, only: wp, ip, max_name_len, cfd_config_base + + implicit none + public :: wp, ip, cfd_config, config_print, config_with_reconstruction + + ! 扩展配置类型 + type, extends(cfd_config_base) :: cfd_config + real(wp) :: left_boundary_value = 1.0_wp + real(wp) :: right_boundary_value = 2.0_wp + real(wp) :: domain_length = 2.0_wp + end type cfd_config + +contains + + subroutine config_print(cfg) + type(cfd_config), intent(in) :: cfg + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(cfg%ic_type) + print *, "Reconstruction: ", trim(cfg%recon_scheme), " (order:", cfg%spatial_order, ")" + print *, "Flux type: ", trim(cfg%flux_type) + print *, "Time integration: RK", cfg%rk_order + print *, "Wave speed: ", cfg%wave_speed + print *, "Final time: ", cfg%final_time + print *, "Time step: ", cfg%dt + print *, "Boundary: ", trim(cfg%boundary_type) + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(cfg, scheme, order) + type(cfd_config), intent(inout) :: cfg + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + integer :: i + + ! 转换为小写 + cfg%recon_scheme = scheme + do i = 1, len_trim(cfg%recon_scheme) + if (cfg%recon_scheme(i:i) >= 'A' .and. cfg%recon_scheme(i:i) <= 'Z') then + cfg%recon_scheme(i:i) = char(ichar(cfg%recon_scheme(i:i)) + 32) + end if + end do + + ! 设置阶数 + if (present(order)) then + cfg%spatial_order = order + else + if (index(cfg%recon_scheme, 'weno') > 0) then + cfg%spatial_order = 5 + else if (trim(cfg%recon_scheme) == 'eno') then + cfg%spatial_order = 3 + end if + end if + + if (cfg%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(cfg%recon_scheme), & + " Order: ", cfg%spatial_order + end if + end subroutine config_with_reconstruction + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/src/infrastructure/domain.f90 b/example/1d-linear-convection/weno3/fortran/registry/02d/src/infrastructure/domain.f90 new file mode 100644 index 00000000..5a97c8a5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/src/infrastructure/domain.f90 @@ -0,0 +1,80 @@ +! src/infrastructure/domain.f90 +module domain_module + use base_modules, only: wp, ip + use mesh_module, only: mesh_type + use config_module, only: cfd_config + + implicit none + private + public :: wp, ip, domain_type, domain_create, is_physical_cell + + type :: domain_type + type(cfd_config), pointer :: config => null() + type(mesh_type), pointer :: mesh => null() + integer(ip) :: nghosts = 0 + integer(ip) :: ist = 1 ! 1-based start index + integer(ip) :: ied = 1 ! 1-based end index (exclusive) + integer(ip) :: ntcells = 0 + contains + procedure :: print_info => domain_print_info + end type domain_type + +contains + + function domain_create(config, mesh) result(domain) + type(cfd_config), target, intent(in) :: config + type(mesh_type), target, intent(in) :: mesh + type(domain_type) :: domain + + domain%config => config + domain%mesh => mesh + + ! 计算ghost层数(参考Julia的_calc_nghosts) + domain%nghosts = calc_nghosts(config) + domain%ist = domain%nghosts + 1 + domain%ied = domain%ist + mesh%ncells - 1 + domain%ntcells = mesh%ncells + 2 * domain%nghosts + + if (config%verbose) then + print *, "[DOMAIN] Created with nghosts = ", domain%nghosts + print *, " Physical cells: ", domain%ist, " to ", domain%ied + print *, " Total cells: ", domain%ntcells + end if + end function domain_create + + function calc_nghosts(config) result(nghosts) + type(cfd_config), intent(in) :: config + integer(ip) :: nghosts + + character(len=max_name_length) :: scheme + + scheme = trim(config%recon_scheme) + + if (scheme == "eno") then + nghosts = config%spatial_order + else if (index(scheme, 'weno') > 0) then + nghosts = config%spatial_order / 2 + 1 + else + print *, "[ERROR] Unknown reconstruction scheme: ", trim(scheme) + nghosts = 2 ! 默认值 + end if + end function calc_nghosts + + logical function is_physical_cell(this, idx) + class(domain_type), intent(in) :: this + integer(ip), intent(in) :: idx + is_physical_cell = (idx >= this%ist .and. idx < this%ied) + end function is_physical_cell + + subroutine domain_print_info(this) + class(domain_type), intent(in) :: this + + print *, "=== Domain Information ===" + print *, "Ghost layers: ", this%nghosts + print *, "Physical cells: ", this%ist, " to ", this%ied - 1 + print *, "Total cells: ", this%ntcells + print *, "Mesh cells: ", this%mesh%ncells + print *, "==========================" + end subroutine domain_print_info + +end module domain_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/02d/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..f810f3a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/src/infrastructure/mesh.f90 @@ -0,0 +1,73 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use base_modules, only: wp, ip + + implicit none + public :: wp, ip, mesh_type, mesh_init, mesh_print_info + + ! 网格类型 + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer(ip) :: ncells = 40 + integer(ip) :: nnodes + integer(ip) :: nx + real(wp), allocatable :: x(:) ! 节点坐标 + real(wp), allocatable :: xcc(:) ! 单元中心坐标 + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer(ip), optional, intent(in) :: ncells + + integer(ip) :: i + + ! 设置参数 + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! 计算 + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! 分配内存 + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! 生成节点坐标 + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! 生成单元中心坐标 + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== Mesh Information ===" + print *, "Domain: [", this%xmin, ", ", this%xmax, "]" + print *, "Cells: ", this%ncells + print *, "Nodes: ", this%nnodes + print *, "dx: ", this%dx + print *, "L: ", this%L + print *, "========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/src/infrastructure/solution.f90 b/example/1d-linear-convection/weno3/fortran/registry/02d/src/infrastructure/solution.f90 new file mode 100644 index 00000000..5f9ed087 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/src/infrastructure/solution.f90 @@ -0,0 +1,114 @@ +! src/infrastructure/solution.f90 +module solution_module + use base_modules, only: wp, ip + use domain_module, only: domain_type + + implicit none + private + public :: wp, ip, solution_type, solution_create, solution_reset + + type :: solution_type + type(domain_type), pointer :: domain => null() + real(wp), allocatable :: u(:) ! 当前解(包含ghost) + real(wp), allocatable :: un(:) ! 旧解 + real(wp), allocatable :: q_face_left(:) ! 左界面值 + real(wp), allocatable :: q_face_right(:)! 右界面值 + real(wp), allocatable :: flux(:) ! 通量 + real(wp), allocatable :: res(:) ! 残差 + contains + procedure :: initialize => solution_initialize + procedure :: update_old_field => solution_update_old_field + procedure :: print_info => solution_print_info + end type solution_type + +contains + + function solution_create(domain) result(solution) + type(domain_type), target, intent(in) :: domain + type(solution_type) :: solution + + integer(ip) :: ncells, nnodes, ntcells + + solution%domain => domain + + ncells = domain%mesh%ncells + nnodes = domain%mesh%nnodes + ntcells = domain%ntcells + + allocate(solution%u(ntcells), source=0.0_wp) + allocate(solution%un(ntcells), source=0.0_wp) + allocate(solution%q_face_left(nnodes), source=0.0_wp) + allocate(solution%q_face_right(nnodes), source=0.0_wp) + allocate(solution%flux(nnodes), source=0.0_wp) + allocate(solution%res(ncells), source=0.0_wp) + + if (domain%config%verbose) then + print *, "[SOLUTION] Created with arrays:" + print *, " u size: ", size(solution%u) + print *, " flux size: ", size(solution%flux) + print *, " res size: ", size(solution%res) + end if + end function solution_create + + subroutine solution_initialize(this, initial_values) + class(solution_type), intent(inout) :: this + real(wp), intent(in), optional :: initial_values(:) + + integer(ip) :: i, idx + type(domain_type), pointer :: domain + + domain => this%domain + + if (present(initial_values)) then + ! 应用初始值到物理区域 + do i = domain%ist, domain%ied - 1 + idx = i - domain%ist + 1 + if (idx <= size(initial_values)) then + this%u(i) = initial_values(idx) + end if + end do + else + ! 默认为0 + this%u = 0.0_wp + end if + + ! 同步旧场 + call this%update_old_field() + + if (domain%config%verbose) then + print *, "[SOLUTION] Initialized" + end if + end subroutine solution_initialize + + subroutine solution_update_old_field(this) + class(solution_type), intent(inout) :: this + this%un = this%u + end subroutine solution_update_old_field + + subroutine solution_reset(this) + class(solution_type), intent(inout) :: this + this%u = 0.0_wp + this%un = 0.0_wp + this%q_face_left = 0.0_wp + this%q_face_right = 0.0_wp + this%flux = 0.0_wp + this%res = 0.0_wp + end subroutine solution_reset + + subroutine solution_print_info(this) + class(solution_type), intent(in) :: this + + print *, "=== Solution Information ===" + print *, "u size: ", size(this%u) + print *, "un size: ", size(this%un) + print *, "q_face_left size: ", size(this%q_face_left) + print *, "q_face_right size: ", size(this%q_face_right) + print *, "flux size: ", size(this%flux) + print *, "res size: ", size(this%res) + if (allocated(this%u)) then + print *, "u range: ", minval(this%u), " to ", maxval(this%u) + end if + print *, "============================" + end subroutine solution_print_info + +end module solution_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/src/manager/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02d/src/manager/CMakeLists.txt new file mode 100644 index 00000000..00c8bf49 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/src/manager/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/manager/CMakeLists.txt +message(STATUS "配置管理器模块...") + +# 创建管理器库 +add_library(manager STATIC + component_manager.f90 + component_factory.f90 +) + +# 明确依赖关系:管理器依赖所有其他模块 +target_link_libraries(manager + PRIVATE + core + infrastructure + reconstructor + flux +) + +# 设置模块输出目录 +set_target_properties(manager PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "管理器模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/src/manager/component_factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/02d/src/manager/component_factory.f90 new file mode 100644 index 00000000..114fedea --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/src/manager/component_factory.f90 @@ -0,0 +1,127 @@ +! src/manager/component_factory.f90 +module component_factory_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + use eno_reconstructor_module, only: eno_reconstructor, create_eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor, create_weno3_reconstructor + use rusanov_flux_module, only: rusanov_flux, create_rusanov_flux + + implicit none + private + public :: wp, create_reconstructor, create_flux_calculator + + ! 错误代码 + integer, parameter :: CM_SUCCESS = 0 + integer, parameter :: CM_ERROR_UNKNOWN_SCHEME = 1 + integer, parameter :: CM_ERROR_UNKNOWN_FLUX = 2 + integer, parameter :: CM_ERROR_INVALID_ORDER = 3 + +contains + + ! ==================== 重构器创建 ==================== + + function create_reconstructor(config, status) result(recon) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(reconstructor_base), allocatable :: recon + + character(len=20) :: scheme + integer :: order, error_code + + scheme = trim(adjustl(config%recon_scheme)) + order = config%spatial_order + + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating reconstructor: ", scheme, " order=", order + end if + + select case(scheme) + case('eno') + allocate(eno_reconstructor :: recon) + select type(recon) + type is(eno_reconstructor) + recon = create_eno_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case('weno3') + allocate(weno3_reconstructor :: recon) + select type(recon) + type is(weno3_reconstructor) + recon = create_weno3_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case default + error_code = CM_ERROR_UNKNOWN_SCHEME + if (config%verbose) then + print *, "[ERROR] Unknown reconstructor scheme: ", scheme + print *, " Available: eno, weno3" + end if + end select + + ! 检查阶数有效性 + if (error_code == CM_SUCCESS) then + if (order < 1) then + error_code = CM_ERROR_INVALID_ORDER + if (config%verbose) then + print *, "[ERROR] Invalid spatial order: ", order + end if + end if + end if + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Reconstructor creation failed" + end if + end function create_reconstructor + + ! ==================== 通量计算器创建 ==================== + + function create_flux_calculator(config, status) result(flux) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(flux_calculator_base), allocatable :: flux + + character(len=20) :: flux_type + integer :: error_code + + flux_type = trim(adjustl(config%flux_type)) + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating flux calculator: ", flux_type + end if + + select case(flux_type) + case('rusanov') + allocate(rusanov_flux :: flux) + select type(flux) + type is(rusanov_flux) + flux = create_rusanov_flux() + flux%wave_speed_default = config%wave_speed + end select + + case default + error_code = CM_ERROR_UNKNOWN_FLUX + if (config%verbose) then + print *, "[ERROR] Unknown flux type: ", flux_type + print *, " Available: rusanov" + end if + end select + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Flux calculator creation failed" + end if + end function create_flux_calculator + +end module component_factory_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/src/manager/component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/02d/src/manager/component_manager.f90 new file mode 100644 index 00000000..9e095c25 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/src/manager/component_manager.f90 @@ -0,0 +1,75 @@ +! src/manager/component_manager.f90 +module component_manager_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use component_factory_module, only: create_reconstructor, create_flux_calculator + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + implicit none + private + public :: wp, component_manager_info, validate_config + public :: create_reconstructor, create_flux_calculator + +contains + + ! ==================== 配置验证 ==================== + + function validate_config(config) result(is_valid) + type(cfd_config), intent(in) :: config + logical :: is_valid + + integer :: status + class(reconstructor_base), allocatable :: test_recon + class(flux_calculator_base), allocatable :: test_flux + + is_valid = .false. + + ! 测试创建重构器 + test_recon = create_reconstructor(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid reconstructor configuration" + end if + return + end if + + ! 测试创建通量计算器 + test_flux = create_flux_calculator(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid flux configuration" + end if + return + end if + + ! 清理测试组件 + if (allocated(test_recon)) deallocate(test_recon) + if (allocated(test_flux)) deallocate(test_flux) + + is_valid = .true. + + if (config%verbose) then + print *, "[CONFIG VALIDATION] Configuration is valid" + end if + end function validate_config + + ! ==================== 信息显示 ==================== + + subroutine component_manager_info() + print *, "=== Component Manager ===" + print *, "Available reconstructors:" + print *, " - eno (orders: 1-7)" + print *, " - weno3 (order: 3)" + print *, "" + print *, "Available flux calculators:" + print *, " - rusanov" + print *, "" + print *, "Features:" + print *, " - Configuration validation" + print *, " - Component creation from config" + print *, " - Error handling with status codes" + print *, "=========================" + end subroutine component_manager_info + +end module component_manager_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02d/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02d/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..3fde7746 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,28 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux STATIC + base.f90 + rusanov.f90 +) + +#target_link_libraries(flux PRIVATE core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 明确设置不使用 /Qm64 选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + set_target_properties(flux PROPERTIES + Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /Qm64" # 明确设置,避免继承问题 + ) +endif() + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/02d/src/numerics/flux/base.f90 new file mode 100644 index 00000000..7080a7ae --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/src/numerics/flux/base.f90 @@ -0,0 +1,30 @@ +!src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + procedure :: print_basic_info => flux_print_basic ! 添加辅助方法 + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + + subroutine flux_print_basic(this) + class(flux_calculator_base), intent(in) :: this + print *, " Name: ", trim(this%name) + end subroutine flux_print_basic + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/02d/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..daa9e3bb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/src/numerics/flux/rusanov.f90 @@ -0,0 +1,41 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux, create_rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + + ! 构造函数接口 + interface rusanov_flux + module procedure create_rusanov_flux + end interface + +contains + + ! 构造函数 + type(rusanov_flux) function create_rusanov_flux() result(this) + this%name = "Rusanov" + this%wave_speed_default = 1.0_real64 + end function create_rusanov_flux + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Flux calculator information:" + call this%print_basic_info() + + ! 添加Rusanov特有信息 + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02d/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..5e4b938d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,22 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor STATIC + base.f90 + eno.f90 + weno3.f90 +) + +target_link_libraries(reconstructor PRIVATE core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/02d/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..53798d02 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/src/numerics/reconstructor/base.f90 @@ -0,0 +1,33 @@ +!src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + procedure :: print_basic_info => reconstructor_print_basic ! 添加一个辅助方法 + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + + subroutine reconstructor_print_basic(this) + class(reconstructor_base), intent(in) :: this + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_print_basic + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/02d/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..f973e8b3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor, create_eno_reconstructor ! ← 添加这个 + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + + ! 构造函数接口 + interface eno_reconstructor + module procedure create_eno_reconstructor + end interface + +contains + + ! 构造函数 + type(eno_reconstructor) function create_eno_reconstructor() result(this) + this%name = "ENO" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_eno_reconstructor + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加ENO特有信息 + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/02d/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..d5b7a747 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno3_reconstructor, create_weno3_reconstructor + + type, extends(reconstructor_base) :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + + ! 构造函数接口 + interface weno3_reconstructor + module procedure create_weno3_reconstructor + end interface + +contains + + ! 构造函数 + type(weno3_reconstructor) function create_weno3_reconstructor() result(this) + this%name = "WENO3" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_weno3_reconstructor + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO3特有信息 + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02d/tests/CMakeLists.txt new file mode 100644 index 00000000..9c8da3d5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/tests/CMakeLists.txt @@ -0,0 +1,69 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +#add_executable(test_minimal_simple test_minimal_simple.f90) +# +#message(STATUS "CMAKE_Fortran_MODULE_DIRECTORY=${CMAKE_Fortran_MODULE_DIRECTORY}") +# +#target_link_libraries( test_minimal_simple +# PRIVATE +# infrastructure +#) +# +#add_executable(test_simple_link test_simple_link.f90) +#target_link_libraries(test_simple_link +# PRIVATE +# reconstructor +# flux +#) +# +# +#add_executable(test_factory_simple test_factory_simple.f90) +#target_link_libraries(test_factory_simple +# PRIVATE +# core +# infrastructure +# reconstructor +# flux +#) + +# 更新测试链接 +#add_executable(test_component_manager test_component_manager.f90) +# +#target_link_libraries(test_component_manager +# PRIVATE +# manager # ← 链接到新的管理器库 +# infrastructure +#) +# +#add_executable(test_architecture test_architecture.f90) +#target_link_libraries(test_architecture +# PRIVATE +# core +# infrastructure +# manager +#) + +#add_executable(test_solver_framework test_solver_framework.f90) +#target_link_libraries(test_solver_framework +# PRIVATE +# core +# infrastructure +# manager +#) +# +#add_executable(test_cfd_architecture test_cfd_architecture.f90) +#target_link_libraries(test_cfd_architecture +# PRIVATE +# base +# core +# infrastructure +#) + + +add_executable(test_basic_only test_basic_only.f90) +target_link_libraries(test_basic_only + PRIVATE + infrastructure + core +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/tests/test_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/02d/tests/test_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/tests/test_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/tests/test_basic_only.f90 b/example/1d-linear-convection/weno3/fortran/registry/02d/tests/test_basic_only.f90 new file mode 100644 index 00000000..20901ddc --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/tests/test_basic_only.f90 @@ -0,0 +1,56 @@ +! tests/test_basic_only.f90 +program test_basic_only + ! 只测试最基本的功能,不依赖复杂模块 + use config_module, only: cfd_config, config_print, wp + use mesh_module, only: mesh_type + use registry_module, only: registry_init, registry_cleanup, & + register_component_simple, list_components + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "=== BASIC TEST - Minimal Functionality ===" + print *, "" + + ! 测试1: 配置 + print *, "1. Testing configuration..." + print *, "----------------------------" + call config_print(config) + print *, "" + + ! 测试2: 网格 + print *, "2. Testing mesh..." + print *, "------------------" + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=5) + print *, "Mesh initialized:" + print *, " Cells: ", mesh%ncells + print *, " Nodes: ", mesh%nnodes + print *, " dx: ", mesh%dx + print *, "" + + ! 测试3: 注册系统 + print *, "3. Testing registry..." + print *, "----------------------" + + call registry_init() + + ! 注册组件(使用简化版本) + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! 列出组件 + call list_components() + print *, "" + + ! 清理 + call registry_cleanup() + + print *, "=== TEST PASSED ===" + print *, "✓ Configuration works" + print *, "✓ Mesh works" + print *, "✓ Registry works" + +end program test_basic_only \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/tests/test_cfd_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/02d/tests/test_cfd_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/tests/test_cfd_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/tests/test_component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/02d/tests/test_component_manager.f90 new file mode 100644 index 00000000..f60c3505 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/tests/test_component_manager.f90 @@ -0,0 +1,111 @@ +! tests/test_component_manager.f90 +program test_component_manager + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use component_manager_module, only: create_reconstructor, create_flux_calculator + use component_manager_module, only: component_manager_info, validate_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + implicit none + + type(cfd_config) :: config + class(reconstructor_base), allocatable :: recon + class(flux_calculator_base), allocatable :: flux + integer :: status + logical :: is_valid + + print *, "=== Component Manager Test ===" + print *, "" + + ! 显示组件管理器信息 + call component_manager_info() + print *, "" + + ! 测试1: 基本配置 + print *, "1. Testing basic ENO3 + Rusanov configuration..." + print *, "-----------------------------------------------" + + config%verbose = .true. + call config_print(config) + + ! 配置ENO3重构 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + call config_print(config) + print *, "" + + ! 验证配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Configuration is valid" + else + print *, "[ERROR] Configuration is invalid" + end if + print *, "" + + ! 测试2: 创建组件 + print *, "2. Testing component creation..." + print *, "--------------------------------" + + ! 创建重构器(带状态检查) + recon = create_reconstructor(config, status) + if (status == 0) then + print *, "[OK] Reconstructor created successfully" + call recon%info() + else + print *, "[ERROR] Failed to create reconstructor, code:", status + end if + print *, "" + + ! 创建通量计算器 + flux = create_flux_calculator(config, status) + if (status == 0) then + print *, "[OK] Flux calculator created successfully" + call flux%info() + else + print *, "[ERROR] Failed to create flux calculator, code:", status + end if + print *, "" + + ! 测试3: WENO3重构测试 + print *, "3. Testing WENO3 configuration..." + print *, "---------------------------------" + + call config_with_reconstruction(config, "weno3", 3) + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] WENO3 configuration is valid" + + ! 创建WENO3重构器 + recon = create_reconstructor(config) + call recon%info() + else + print *, "[ERROR] WENO3 configuration is invalid" + end if + print *, "" + + ! 测试4: 错误配置测试 + print *, "4. Testing invalid configuration..." + print *, "-----------------------------------" + + config%recon_scheme = "unknown_scheme" + config%flux_type = "unknown_flux" + + is_valid = validate_config(config) + if (.not. is_valid) then + print *, "[OK] Invalid configuration correctly rejected" + else + print *, "[ERROR] Invalid configuration should have been rejected" + end if + + ! 清理 + if (allocated(recon)) deallocate(recon) + if (allocated(flux)) deallocate(flux) + + print *, "" + print *, "=== Component manager test completed successfully ===" + +end program test_component_manager \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/tests/test_factory_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/02d/tests/test_factory_simple.f90 new file mode 100644 index 00000000..d4139bb1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/tests/test_factory_simple.f90 @@ -0,0 +1,86 @@ +!tests/test_factory_simple.f90 +program test_factory_simple + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module, only: initialize_registry, cleanup_registry, & + register_component_simple, has_component + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Basic systems + print *, "1. Testing basic systems..." + print *, "-----------------------------" + call config_print(config) + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Creating reconstructors + print *, "2. Testing reconstructors..." + print *, "------------------------------" + + ! 创建并测试ENO重构器 + print *, "Creating ENO reconstructor..." + eno = eno_reconstructor() ! 使用构造函数 + call eno%info() ! 必须调用info方法 + + print *, "" + print *, "Creating WENO3 reconstructor..." + weno3 = weno3_reconstructor() ! 使用构造函数 + call weno3%info() ! 必须调用info方法 + print *, "" + + ! Test 3: Creating flux calculator + print *, "3. Testing flux calculator..." + print *, "-------------------------------" + + print *, "Creating Rusanov flux calculator..." + rusanov = rusanov_flux() ! 使用构造函数 + call rusanov%info() ! 必须调用info方法 + print *, "" + + ! Test 4: Registry integration + print *, "4. Testing registry..." + print *, "----------------------" + + call initialize_registry(verbose=.true.) + + ! Register components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! Check registration + if (has_component("reconstructor", "eno")) then + print *, "[OK] ENO reconstructor registered successfully" + end if + + if (has_component("reconstructor", "weno3")) then + print *, "[OK] WENO3 reconstructor registered successfully" + end if + + if (has_component("flux", "rusanov")) then + print *, "[OK] Rusanov flux registered successfully" + end if + + print *, "" + call cleanup_registry() + + print *, "=== Factory pattern simple test completed successfully ===" + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/tests/test_minimal_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/02d/tests/test_minimal_simple.f90 new file mode 100644 index 00000000..cc6753b5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/tests/test_minimal_simple.f90 @@ -0,0 +1,84 @@ +! tests/test_minimal_simple.f90 +program test_minimal_simple + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Simple Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_simple() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components (simple version) + print *, "5. Testing registry functionality" + print *, "---------------------------------" + print *, "Registry initialized: ", registry_is_initialized() + print *, "Number of components: ", registry_get_size() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Simple test completed successfully ===" + +end program test_minimal_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/tests/test_simple_link.f90 b/example/1d-linear-convection/weno3/fortran/registry/02d/tests/test_simple_link.f90 new file mode 100644 index 00000000..27b165b0 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/tests/test_simple_link.f90 @@ -0,0 +1,108 @@ +! tests/test_minimal.f90 +program test_minimal + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i ! Declare loop variable here + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call initialize_registry(verbose=.true.) + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call component_registry%list_simple() + print *, "Registry size: ", component_registry%size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components + print *, "5. Testing available components" + print *, "--------------------------------" + + ! Use a separate block to avoid allocation issues + call test_available_components() + print *, "" + + ! Cleanup + call cleanup_registry() + + print *, "=== Minimal test completed successfully ===" + +contains + + ! Internal subroutine for testing available components + subroutine test_available_components() + character(len=:), allocatable :: names(:) + integer, allocatable :: orders(:) + integer :: j + + call get_available_components("reconstructor", names, orders) + + if (allocated(names)) then + print *, "Reconstructors:" + do j = 1, size(names) + print *, " - ", trim(names(j)) + if (allocated(orders)) then + print *, " Order: ", orders(j) + end if + end do + else + print *, "No reconstructors found" + end if + end subroutine test_available_components + +end program test_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02d/tests/test_solver_framework.f90 b/example/1d-linear-convection/weno3/fortran/registry/02d/tests/test_solver_framework.f90 new file mode 100644 index 00000000..6754323d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02d/tests/test_solver_framework.f90 @@ -0,0 +1,91 @@ +! tests/test_solver_framework.f90 +program test_solver_framework + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use mesh_module, only: mesh_type + use solver_module, only: cfd_solver, solver_create, solver_run, solver_cleanup + use solver_module, only: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, & + SOLVER_COMPLETED, SOLVER_ERROR + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(cfd_solver) :: solver + + print *, "=== 求解器框架测试 ===" + print *, "" + + ! 测试1: 基本创建 + print *, "1. 测试求解器创建..." + print *, "----------------------" + + ! 创建配置 + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.0_real64 + config%dt = 0.01_real64 + + call config_print(config) + print *, "" + + ! 创建网格 + call mesh%init(xmin=0.0_real64, xmax=2.0_real64, ncells=20) + call mesh%print_info() + print *, "" + + ! 创建求解器 + solver = solver_create(config, mesh) + print *, "✓ 求解器创建成功" + print *, " 状态: ", solver%get_state() + print *, "" + + ! 测试2: 求解器初始化 + print *, "2. 测试求解器初始化..." + print *, "------------------------" + + call solver%initialize() + print *, "✓ 求解器初始化完成" + print *, " 状态: ", solver%get_state() + print *, " 错误信息: '", trim(solver%get_error()), "'" + print *, "" + + ! 测试3: 简单运行 + print *, "3. 测试求解器运行..." + print *, "----------------------" + + call solver_run(solver, 0.05_real64) ! 运行到0.05秒 + print *, "✓ 求解器运行完成" + print *, " 最终状态: ", solver%get_state() + print *, "" + + ! 测试4: 清理 + print *, "4. 测试求解器清理..." + print *, "----------------------" + + call solver_cleanup(solver) + print *, "✓ 求解器清理完成" + print *, " 状态: ", solver%get_state() + print *, "" + + ! 测试5: 错误处理 + print *, "5. 测试错误处理..." + print *, "-------------------" + + ! 尝试重复初始化 + call solver%initialize() + print *, " 重复初始化状态: ", solver%get_state() + print *, " 错误信息: '", trim(solver%get_error()), "'" + + call solver_cleanup(solver) + print *, "" + + print *, "=== 框架测试总结 ===" + print *, "✓ 求解器创建/初始化/运行/清理流程验证完成" + print *, "✓ 状态管理正常工作" + print *, "✓ 错误处理机制就绪" + print *, "" + print *, "下一步: 添加实际数值计算功能" + +end program test_solver_framework \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02e/CMakeLists.txt new file mode 100644 index 00000000..ef66d584 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/README.md b/example/1d-linear-convection/weno3/fortran/registry/02e/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/02e/scripts/build.bat new file mode 100644 index 00000000..6fd6dc03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/scripts/build.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Project Builder +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python build script with full Intel environment support... +echo. + +python build.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Build failed + pause + exit /b 1 +) + +echo. +echo [INFO] Build completed successfully! +echo. +echo [INFO] To run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/02e/scripts/build.py new file mode 100644 index 00000000..3bf6d537 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/scripts/build.py @@ -0,0 +1,629 @@ +#!/usr/bin/env python3 +""" +Fortran CFD Project Builder - 完整Python解决方案 +在Python内部处理Intel oneAPI环境配置 +""" + +import os +import sys +import subprocess +import shutil +import argparse +import time +import platform +import tempfile +from pathlib import Path + +class IntelEnvironment: + """Intel oneAPI环境管理器""" + + def __init__(self): + self.setvars_path = None + self.env_vars = {} + + def find_setvars(self): + """查找setvars.bat文件""" + possible_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + r"C:\Program Files (x86)\Intel\oneAPI\compiler\latest\env\vars.bat", + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\setvars.bat"), + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\compiler\latest\env\vars.bat"), + ] + + for path in possible_paths: + if os.path.exists(path): + self.setvars_path = path + return True + + return False + + def setup_environment(self): + """设置Intel环境""" + if not self.find_setvars(): + return False + + try: + # 创建临时的批处理文件来捕获环境变量 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + f.write(f'@echo off\n') + f.write(f'call "{self.setvars_path}" >nul 2>&1\n') + f.write(f'set\n') # 输出所有环境变量 + temp_bat = f.name + + # 运行批处理文件并捕获输出 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + shell=True + ) + + # 解析环境变量 + for line in result.stdout.split('\n'): + line = line.strip() + if '=' in line: + key, value = line.split('=', 1) + self.env_vars[key.strip()] = value.strip() + + # 清理临时文件 + os.unlink(temp_bat) + + # 更新当前进程的环境变量 + os.environ.update(self.env_vars) + + return True + + except Exception as e: + print(f"设置Intel环境失败: {e}") + return False + + def get_compiler_info(self): + """获取编译器信息""" + info = {} + + # 检查ifx编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + env={**os.environ, **self.env_vars} if self.env_vars else os.environ + ) + + if result.returncode == 0: + for line in result.stdout.split('\n'): + if 'Version' in line or '版本' in line: + info['ifx_version'] = line.strip() + break + except: + pass + + # 检查环境变量 + info['ifx_root'] = self.env_vars.get('IFX_ROOT', '') + info['compiler_root'] = self.env_vars.get('ONEAPI_ROOT', '') + + return info + +class BuildSystem: + """构建系统主类""" + + def __init__(self): + self.project_root = Path(__file__).parent.parent + self.build_dir = self.project_root / "build" + self.intel_env = IntelEnvironment() + + # 设置控制台编码 + if sys.platform == "win32": + try: + import ctypes + # 设置控制台输出为UTF-8 + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + except: + pass + + def print_header(self, text): + """打印标题""" + print(f"\n{'='*70}") + print(f" {text}") + print(f"{'='*70}\n") + + def print_step(self, step, total, message): + """打印步骤""" + print(f"[{step}/{total}] {message}...") + + def print_success(self, message): + """打印成功""" + print(f"\033[92m✓ {message}\033[0m") + + def print_error(self, message): + """打印错误""" + print(f"\033[91m✗ {message}\033[0m") + + def print_warning(self, message): + """打印警告""" + print(f"\033[93m! {message}\033[0m") + + def print_info(self, message): + """打印信息""" + print(f"\033[94mℹ {message}\033[0m") + + def check_prerequisites(self): + """检查前提条件""" + self.print_step(1, 6, "检查前提条件") + + # 检查Python版本 + python_version = sys.version.split()[0] + self.print_info(f"Python版本: {python_version}") + + # 检查平台 + self.print_info(f"平台: {platform.system()} {platform.release()}") + self.print_info(f"处理器核心数: {os.cpu_count()}") + + # 检查CMake + try: + result = subprocess.run( + ["cmake", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + version_line = result.stdout.split('\n')[0] + self.print_success(f"CMake: {version_line}") + else: + self.print_error("CMake未找到") + return False + except FileNotFoundError: + self.print_error("CMake未安装") + return False + + return True + + def setup_intel_environment(self, args): + """设置Intel环境""" + self.print_step(2, 6, "配置Intel oneAPI环境") + + if not self.intel_env.find_setvars(): + self.print_warning("未找到Intel oneAPI setvars.bat") + self.print_info("将尝试使用系统环境中的编译器") + + # 检查是否能直接访问编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + self.print_success("Intel编译器在系统PATH中找到") + return True + else: + self.print_warning("Intel编译器未在PATH中找到") + except: + self.print_warning("无法访问Intel编译器") + + return True # 继续,让CMake自己找编译器 + + # 设置环境 + if self.intel_env.setup_environment(): + compiler_info = self.intel_env.get_compiler_info() + + if compiler_info.get('ifx_version'): + self.print_success(f"Intel Fortran编译器: {compiler_info['ifx_version']}") + elif compiler_info.get('ifx_root'): + self.print_success(f"Intel编译器路径: {compiler_info['ifx_root']}") + else: + self.print_success("Intel oneAPI环境配置完成") + + return True + else: + self.print_warning("Intel环境配置失败,将继续使用系统环境") + return True + + def clean_build_directory(self, args): + """清理构建目录""" + if args.clean and self.build_dir.exists(): + self.print_info("清理构建目录...") + try: + shutil.rmtree(self.build_dir) + self.print_success("构建目录已清理") + except Exception as e: + self.print_error(f"清理失败: {e}") + if not args.force: + return False + return True + + def run_command(self, cmd, cwd=None, check=True, env=None): + """运行命令""" + if isinstance(cmd, list): + cmd_str = ' '.join(str(c) for c in cmd if c) + else: + cmd_str = str(cmd) + + print(f" \033[96m$\033[0m {cmd_str}") + + try: + # 合并环境变量 + exec_env = os.environ.copy() + if env: + exec_env.update(env) + if self.intel_env.env_vars: + exec_env.update(self.intel_env.env_vars) + + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=False, + env=exec_env + ) + + # 处理输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or '完成' in line or '生成' in line: + print(f" \033[92m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + if check and result.returncode != 0: + self.print_error(f"命令执行失败,退出码: {result.returncode}") + return False + + return True + + except Exception as e: + self.print_error(f"命令执行异常: {e}") + return False + + def configure_cmake(self, args): + """配置CMake""" + self.print_step(3, 6, "配置CMake项目") + + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ] + + if args.compiler == "ifx": + cmake_cmd.extend(["-T", "fortran=ifx"]) + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + success = self.run_command(cmake_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("CMake配置完成") + else: + self.print_error("CMake配置失败") + + return success + + def build_project(self, args): + """构建项目""" + self.print_step(4, 6, "构建项目") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + success = self.run_command(build_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("项目构建完成") + else: + self.print_error("构建失败") + + return success + + def run_tests_with_environment(self, test_exe): + """运行单个测试,确保有Intel环境""" + try: + # 创建临时的批处理文件来运行测试 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'"{test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'"{test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + return result + + except Exception as e: + print(f"运行测试失败: {e}") + return None + + def run_tests(self, args): + """运行测试""" + self.print_step(5, 6, "运行测试") + + # 查找测试可执行文件 + test_dir = self.build_dir / "bin" / args.build_type + if not test_dir.exists(): + test_dir = self.build_dir / "bin" + if not test_dir.exists(): + test_dir = self.build_dir + + test_files = list(test_dir.glob("test_*.exe")) + + if not test_files: + self.print_warning("未找到测试程序") + return True + + all_passed = True + + for test_exe in sorted(test_files): + test_name = test_exe.stem + self.print_info(f"运行测试: {test_name}") + print(f" {'-'*50}") + + # 运行测试 + result = self.run_tests_with_environment(str(test_exe)) + + if result is None: + self.print_error(f" {test_name} 运行失败") + all_passed = False + continue + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + print(f" {line}") + + if result.returncode == 0: + self.print_success(f" {test_name} 通过") + else: + self.print_error(f" {test_name} 失败 (退出码: {result.returncode})") + all_passed = False + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + print() # 空行 + + return all_passed + + def create_test_runner(self, args): + """创建独立的测试运行器""" + self.print_step(6, 6, "创建测试运行器") + + runner_path = self.build_dir / "run_tests.bat" + + content = f'''@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Test Runner +echo ======================================== +echo. + +REM Setup Intel oneAPI environment +set "SETVARS_PATH={self.intel_env.setvars_path or ''}" +if exist "%SETVARS_PATH%" ( + call "%SETVARS_PATH%" >nul + echo [INFO] Intel environment configured +) else ( + echo [WARNING] Intel environment not found + echo [WARNING] Tests may fail without runtime libraries +) + +echo. + +REM Run all test executables +set "TEST_COUNT=0" +set "PASS_COUNT=0" + +for %%f in ("bin\\{args.build_type}\\test_*.exe") do ( + set /a TEST_COUNT+=1 + echo [TEST %%f] + echo {'-'*50} + + %%f + if errorlevel 1 ( + echo [FAILED] %%f + ) else ( + echo [PASSED] %%f + set /a PASS_COUNT+=1 + ) + echo. +) + +echo ======================================== +echo Tests: %PASS_COUNT%/%TEST_COUNT% passed +if %PASS_COUNT% equ %TEST_COUNT% ( + echo [SUCCESS] All tests passed! +) else ( + echo [FAILURE] Some tests failed +) +echo ======================================== + +pause +''' + + with open(runner_path, 'w', encoding='utf-8') as f: + f.write(content) + + self.print_success(f"测试运行器已创建: {runner_path}") + self.print_info(f"使用方法: cd build && run_tests.bat") + + return runner_path + + def generate_report(self, args, build_time, tests_passed): + """生成构建报告""" + self.print_header("构建完成") + + print(f"项目: {self.project_root.name}") + print(f"构建类型: {args.build_type}") + print(f"编译器: {args.compiler}") + print(f"并行作业: {args.jobs}") + print(f"总耗时: {build_time:.1f}秒") + print(f"测试结果: {'全部通过' if tests_passed else '有失败'}") + + # 显示生成的可执行文件 + bin_dir = self.build_dir / "bin" / args.build_type + if bin_dir.exists(): + print(f"\n生成的可执行文件:") + for exe in sorted(bin_dir.glob("*.exe")): + size_mb = exe.stat().st_size / (1024 * 1024) + print(f" • {exe.name} ({size_mb:.2f} MB)") + + # 显示测试运行器信息 + runner_path = self.build_dir / "run_tests.bat" + if runner_path.exists(): + print(f"\n独立测试运行器:") + print(f" • {runner_path.name}") + print(f" 在Intel oneAPI环境中运行所有测试") + + def run(self): + """运行构建系统""" + parser = argparse.ArgumentParser( + description="Fortran CFD项目构建工具", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认构建 + %(prog)s --clean # 清理后构建 + %(prog)s --build-type Release # Release构建 + %(prog)s --no-tests # 只构建,不运行测试 + %(prog)s -j8 --verbose # 8线程并行构建,详细输出 + """ + ) + + parser.add_argument("--build-type", choices=["Debug", "Release"], + default="Debug", help="构建类型") + parser.add_argument("--compiler", choices=["ifx", "ifort"], + default="ifx", help="Fortran编译器") + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-tests", action="store_true", + help="跳过测试") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + + args = parser.parse_args() + + # 开始构建 + start_time = time.time() + + self.print_header("Fortran CFD 项目构建系统") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 检查前提条件 + if not self.check_prerequisites(): + if not args.force: + return 1 + + # 2. 设置Intel环境 + if not self.setup_intel_environment(args): + if not args.force: + return 1 + + # 3. 清理目录 + if not self.clean_build_directory(args): + if not args.force: + return 1 + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 4. 配置CMake + if not self.configure_cmake(args): + if not args.force: + return 1 + + # 5. 构建项目 + if not self.build_project(args): + if not args.force: + return 1 + + # 6. 运行测试和创建测试运行器 + tests_passed = True + if not args.no_tests: + tests_passed = self.run_tests(args) + + # 创建测试运行器 + self.create_test_runner(args) # 传递 args 参数 + + # 7. 生成报告 + build_time = time.time() - start_time + self.generate_report(args, build_time, tests_passed) + + return 0 if tests_passed else 1 + + except KeyboardInterrupt: + self.print_error("\n构建被用户中断") + return 1 + except Exception as e: + self.print_error(f"构建过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + builder = BuildSystem() + return builder.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/02e/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02e/src/CMakeLists.txt new file mode 100644 index 00000000..d8cbd5f3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/src/CMakeLists.txt @@ -0,0 +1,16 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(base) +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) +add_subdirectory(manager) + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/src/base/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02e/src/base/CMakeLists.txt new file mode 100644 index 00000000..d537affd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/src/base/CMakeLists.txt @@ -0,0 +1,15 @@ +# src/base/CMakeLists.txt +message(STATUS "Configuring base module...") + +add_library(base STATIC + modules.f90 +) + +set_target_properties(base PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Base module configured") + +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/src/base/modules.f90 b/example/1d-linear-convection/weno3/fortran/registry/02e/src/base/modules.f90 new file mode 100644 index 00000000..99ef3337 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/src/base/modules.f90 @@ -0,0 +1,42 @@ +! src/base/modules.f90 +module base_modules + use, intrinsic :: iso_fortran_env, only: real64, int32 + + implicit none + + ! 公开所有基本类型 + public :: wp, ip, string_len, max_name_len + public :: cfd_config_base, component_info + + ! 工作精度类型 + integer, parameter :: wp = real64 + integer, parameter :: ip = int32 + + ! 字符串长度常量(使用数值常量) + integer, parameter :: string_len = 100 + integer, parameter :: max_name_len = 32 + + ! 基础配置类型 + type :: cfd_config_base + character(len=max_name_len) :: ic_type = "step" + character(len=max_name_len) :: recon_scheme = "eno" + character(len=max_name_len) :: flux_type = "rusanov" + integer(ip) :: rk_order = 1 + real(wp) :: wave_speed = 1.0_wp + real(wp) :: final_time = 0.625_wp + real(wp) :: dt = 0.025_wp + character(len=max_name_len) :: boundary_type = "periodic" + integer(ip) :: spatial_order = 2 + character(len=max_name_len) :: equation_type = "linear_advection" + character(len=max_name_len) :: problem_type = "linear_advection" + logical :: verbose = .true. + end type cfd_config_base + + ! 组件信息类型 + type :: component_info + character(len=max_name_len) :: category = "" + character(len=max_name_len) :: name = "" + integer(ip) :: order = 0 + end type component_info + +end module base_modules \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02e/src/core/CMakeLists.txt new file mode 100644 index 00000000..d8b8df06 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/src/core/CMakeLists.txt @@ -0,0 +1,14 @@ +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") + +add_library(core STATIC + registry.f90 +) + +target_link_libraries(core PRIVATE base) + +set_target_properties(core PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Core module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/src/core/factory_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/02e/src/core/factory_base.f90 new file mode 100644 index 00000000..302418a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/src/core/factory_base.f90 @@ -0,0 +1,57 @@ +! src/core/factory_base.f90 +module factory_base_module + use base_modules, only: wp, ip + use registry_module, only: create_component, has_component + + implicit none + private + public :: wp, ip, factory_base, factory_create + + ! 工厂基类 + type :: factory_base + character(len=max_name_length) :: category = "" + contains + procedure :: create => factory_base_create + procedure :: get_available => factory_base_get_available + end type factory_base + + ! 便捷函数类型 + abstract interface + function factory_function_interface(category, name) result(instance) + import :: wp + character(len=*), intent(in) :: category, name + class(*), allocatable :: instance + end function factory_function_interface + end interface + +contains + + ! 创建工厂实例 + function factory_create(category) result(factory) + character(len=*), intent(in) :: category + type(factory_base) :: factory + factory%category = trim(category) + end function factory_create + + ! 工厂创建方法 + function factory_base_create(this, name) result(instance) + class(factory_base), intent(in) :: this + character(len=*), intent(in) :: name + class(*), allocatable :: instance + + instance = create_component(this%category, name) + end function factory_base_create + + ! 获取可用组件列表(简化版) + subroutine factory_base_get_available(this, names, count) + class(factory_base), intent(in) :: this + character(len=*), allocatable, intent(out) :: names(:) + integer(ip), intent(out) :: count + + ! 这里需要实现从注册表获取列表的逻辑 + ! 暂时返回空列表 + count = 0 + allocate(character(len=max_name_length) :: names(0)) + end subroutine factory_base_get_available + +end module factory_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/02e/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..a960167c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/02e/src/core/registry.f90 new file mode 100644 index 00000000..acc63edb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/src/core/registry.f90 @@ -0,0 +1,203 @@ +! src/core/registry.f90 +module registry_module + use base_modules, only: wp, ip, max_name_len, component_info + + implicit none + private + + ! 明确公开所有需要的接口 + public :: wp, ip ! 类型参数 + public :: component_info ! 类型 + public :: registry_init, registry_cleanup ! 初始化/清理 + public :: register_component_simple ! 注册组件 + public :: has_component_simple ! 检查组件 + public :: list_components ! 列出组件 + public :: registry_is_initialized ! 检查初始化状态 ← 新增 + public :: registry_get_size ! 获取大小 ← 新增 + + ! 全局注册表 + type :: component_registry + type(component_info), allocatable :: components(:) + integer(ip) :: count = 0 + integer(ip) :: capacity = 100 + logical :: initialized = .false. + logical :: verbose = .true. + end type component_registry + + type(component_registry) :: registry + +contains + + ! ==================== 公共API ==================== + + subroutine registry_init(verbose) + logical, optional, intent(in) :: verbose + + if (registry%initialized) then + if (registry%verbose) then + print *, "[REGISTRY] Already initialized" + end if + return + end if + + if (present(verbose)) then + registry%verbose = verbose + end if + + allocate(registry%components(registry%capacity)) + registry%initialized = .true. + + if (registry%verbose) then + print *, "[REGISTRY] Initialized with capacity:", registry%capacity + end if + end subroutine registry_init + + subroutine registry_cleanup() + if (allocated(registry%components)) then + deallocate(registry%components) + end if + registry%initialized = .false. + registry%count = 0 + + if (registry%verbose) then + print *, "[REGISTRY] Cleaned up" + end if + end subroutine registry_cleanup + + subroutine register_component_simple(category, name, order) + character(len=*), intent(in) :: category, name + integer(ip), optional, intent(in) :: order + + integer(ip) :: i + type(component_info) :: info + + if (.not. registry%initialized) then + call registry_init() + end if + + ! 检查是否已存在 + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + if (registry%verbose) then + print *, "[WARN] Overwriting component: ", trim(category), ".", trim(name) + end if + + ! 更新 + if (present(order)) then + registry%components(i)%order = order + else + registry%components(i)%order = 0 + end if + return + end if + end do + + ! 扩展数组 + if (registry%count >= registry%capacity) then + call expand_registry() + end if + + ! 添加新组件 + registry%count = registry%count + 1 + + info%category = trim(category) + info%name = trim(name) + info%order = 0 + if (present(order)) then + info%order = order + end if + + registry%components(registry%count) = info + + if (registry%verbose) then + print *, "[OK] Registered simple: ", trim(category), ".", trim(name) + end if + end subroutine register_component_simple + + logical function has_component_simple(category, name) + character(len=*), intent(in) :: category, name + + integer(ip) :: i + + has_component_simple = .false. + + if (.not. registry%initialized) return + + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + has_component_simple = .true. + return + end if + end do + end function has_component_simple + + subroutine list_components(category) + character(len=*), optional, intent(in) :: category + + integer(ip) :: i, count + + if (.not. registry%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + if (registry%count == 0) then + print *, "[INFO] No components registered" + return + end if + + count = 0 + print *, "=== Registry Contents ===" + do i = 1, registry%count + if (.not. present(category) .or. & + trim(registry%components(i)%category) == trim(category)) then + call print_component_info(registry%components(i)) + count = count + 1 + end if + end do + + print *, "Total:", count, "components" + print *, "==========================" + end subroutine list_components + + ! ==================== 新增函数 ==================== + + logical function registry_is_initialized() + ! 检查注册表是否已初始化 + registry_is_initialized = registry%initialized + end function registry_is_initialized + + integer(ip) function registry_get_size() + ! 获取注册表中的组件数量 + registry_get_size = registry%count + end function registry_get_size + + ! ==================== 内部辅助函数 ==================== + + subroutine expand_registry() + type(component_info), allocatable :: temp(:) + + registry%capacity = registry%capacity * 2 + allocate(temp(registry%capacity)) + temp(1:registry%count) = registry%components(1:registry%count) + call move_alloc(temp, registry%components) + + if (registry%verbose) then + print *, "[INFO] Registry expanded to capacity:", registry%capacity + end if + end subroutine expand_registry + + subroutine print_component_info(info) + type(component_info), intent(in) :: info + + if (info%order > 0) then + print *, " [", trim(info%category), ".", trim(info%name), & + " (order:", info%order, ")]" + else + print *, " [", trim(info%category), ".", trim(info%name), "]" + end if + end subroutine print_component_info + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02e/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..c48396af --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/src/infrastructure/CMakeLists.txt @@ -0,0 +1,15 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "Configuring infrastructure module...") + +add_library(infrastructure STATIC + config.f90 + mesh.f90 +) + +target_link_libraries(infrastructure PRIVATE base) + +set_target_properties(infrastructure PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Infrastructure module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/02e/src/infrastructure/config.f90 new file mode 100644 index 00000000..719e14d2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/src/infrastructure/config.f90 @@ -0,0 +1,64 @@ +! src/infrastructure/config.f90 +module config_module + use base_modules, only: wp, ip, max_name_len, cfd_config_base + + implicit none + public :: wp, ip, cfd_config, config_print, config_with_reconstruction + + ! 扩展配置类型 + type, extends(cfd_config_base) :: cfd_config + real(wp) :: left_boundary_value = 1.0_wp + real(wp) :: right_boundary_value = 2.0_wp + real(wp) :: domain_length = 2.0_wp + end type cfd_config + +contains + + subroutine config_print(cfg) + type(cfd_config), intent(in) :: cfg + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(cfg%ic_type) + print *, "Reconstruction: ", trim(cfg%recon_scheme), " (order:", cfg%spatial_order, ")" + print *, "Flux type: ", trim(cfg%flux_type) + print *, "Time integration: RK", cfg%rk_order + print *, "Wave speed: ", cfg%wave_speed + print *, "Final time: ", cfg%final_time + print *, "Time step: ", cfg%dt + print *, "Boundary: ", trim(cfg%boundary_type) + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(cfg, scheme, order) + type(cfd_config), intent(inout) :: cfg + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + integer :: i + + ! 转换为小写 + cfg%recon_scheme = scheme + do i = 1, len_trim(cfg%recon_scheme) + if (cfg%recon_scheme(i:i) >= 'A' .and. cfg%recon_scheme(i:i) <= 'Z') then + cfg%recon_scheme(i:i) = char(ichar(cfg%recon_scheme(i:i)) + 32) + end if + end do + + ! 设置阶数 + if (present(order)) then + cfg%spatial_order = order + else + if (index(cfg%recon_scheme, 'weno') > 0) then + cfg%spatial_order = 5 + else if (trim(cfg%recon_scheme) == 'eno') then + cfg%spatial_order = 3 + end if + end if + + if (cfg%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(cfg%recon_scheme), & + " Order: ", cfg%spatial_order + end if + end subroutine config_with_reconstruction + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/src/infrastructure/domain.f90 b/example/1d-linear-convection/weno3/fortran/registry/02e/src/infrastructure/domain.f90 new file mode 100644 index 00000000..5a97c8a5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/src/infrastructure/domain.f90 @@ -0,0 +1,80 @@ +! src/infrastructure/domain.f90 +module domain_module + use base_modules, only: wp, ip + use mesh_module, only: mesh_type + use config_module, only: cfd_config + + implicit none + private + public :: wp, ip, domain_type, domain_create, is_physical_cell + + type :: domain_type + type(cfd_config), pointer :: config => null() + type(mesh_type), pointer :: mesh => null() + integer(ip) :: nghosts = 0 + integer(ip) :: ist = 1 ! 1-based start index + integer(ip) :: ied = 1 ! 1-based end index (exclusive) + integer(ip) :: ntcells = 0 + contains + procedure :: print_info => domain_print_info + end type domain_type + +contains + + function domain_create(config, mesh) result(domain) + type(cfd_config), target, intent(in) :: config + type(mesh_type), target, intent(in) :: mesh + type(domain_type) :: domain + + domain%config => config + domain%mesh => mesh + + ! 计算ghost层数(参考Julia的_calc_nghosts) + domain%nghosts = calc_nghosts(config) + domain%ist = domain%nghosts + 1 + domain%ied = domain%ist + mesh%ncells - 1 + domain%ntcells = mesh%ncells + 2 * domain%nghosts + + if (config%verbose) then + print *, "[DOMAIN] Created with nghosts = ", domain%nghosts + print *, " Physical cells: ", domain%ist, " to ", domain%ied + print *, " Total cells: ", domain%ntcells + end if + end function domain_create + + function calc_nghosts(config) result(nghosts) + type(cfd_config), intent(in) :: config + integer(ip) :: nghosts + + character(len=max_name_length) :: scheme + + scheme = trim(config%recon_scheme) + + if (scheme == "eno") then + nghosts = config%spatial_order + else if (index(scheme, 'weno') > 0) then + nghosts = config%spatial_order / 2 + 1 + else + print *, "[ERROR] Unknown reconstruction scheme: ", trim(scheme) + nghosts = 2 ! 默认值 + end if + end function calc_nghosts + + logical function is_physical_cell(this, idx) + class(domain_type), intent(in) :: this + integer(ip), intent(in) :: idx + is_physical_cell = (idx >= this%ist .and. idx < this%ied) + end function is_physical_cell + + subroutine domain_print_info(this) + class(domain_type), intent(in) :: this + + print *, "=== Domain Information ===" + print *, "Ghost layers: ", this%nghosts + print *, "Physical cells: ", this%ist, " to ", this%ied - 1 + print *, "Total cells: ", this%ntcells + print *, "Mesh cells: ", this%mesh%ncells + print *, "==========================" + end subroutine domain_print_info + +end module domain_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/02e/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..f810f3a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/src/infrastructure/mesh.f90 @@ -0,0 +1,73 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use base_modules, only: wp, ip + + implicit none + public :: wp, ip, mesh_type, mesh_init, mesh_print_info + + ! 网格类型 + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer(ip) :: ncells = 40 + integer(ip) :: nnodes + integer(ip) :: nx + real(wp), allocatable :: x(:) ! 节点坐标 + real(wp), allocatable :: xcc(:) ! 单元中心坐标 + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer(ip), optional, intent(in) :: ncells + + integer(ip) :: i + + ! 设置参数 + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! 计算 + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! 分配内存 + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! 生成节点坐标 + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! 生成单元中心坐标 + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== Mesh Information ===" + print *, "Domain: [", this%xmin, ", ", this%xmax, "]" + print *, "Cells: ", this%ncells + print *, "Nodes: ", this%nnodes + print *, "dx: ", this%dx + print *, "L: ", this%L + print *, "========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/src/infrastructure/solution.f90 b/example/1d-linear-convection/weno3/fortran/registry/02e/src/infrastructure/solution.f90 new file mode 100644 index 00000000..5f9ed087 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/src/infrastructure/solution.f90 @@ -0,0 +1,114 @@ +! src/infrastructure/solution.f90 +module solution_module + use base_modules, only: wp, ip + use domain_module, only: domain_type + + implicit none + private + public :: wp, ip, solution_type, solution_create, solution_reset + + type :: solution_type + type(domain_type), pointer :: domain => null() + real(wp), allocatable :: u(:) ! 当前解(包含ghost) + real(wp), allocatable :: un(:) ! 旧解 + real(wp), allocatable :: q_face_left(:) ! 左界面值 + real(wp), allocatable :: q_face_right(:)! 右界面值 + real(wp), allocatable :: flux(:) ! 通量 + real(wp), allocatable :: res(:) ! 残差 + contains + procedure :: initialize => solution_initialize + procedure :: update_old_field => solution_update_old_field + procedure :: print_info => solution_print_info + end type solution_type + +contains + + function solution_create(domain) result(solution) + type(domain_type), target, intent(in) :: domain + type(solution_type) :: solution + + integer(ip) :: ncells, nnodes, ntcells + + solution%domain => domain + + ncells = domain%mesh%ncells + nnodes = domain%mesh%nnodes + ntcells = domain%ntcells + + allocate(solution%u(ntcells), source=0.0_wp) + allocate(solution%un(ntcells), source=0.0_wp) + allocate(solution%q_face_left(nnodes), source=0.0_wp) + allocate(solution%q_face_right(nnodes), source=0.0_wp) + allocate(solution%flux(nnodes), source=0.0_wp) + allocate(solution%res(ncells), source=0.0_wp) + + if (domain%config%verbose) then + print *, "[SOLUTION] Created with arrays:" + print *, " u size: ", size(solution%u) + print *, " flux size: ", size(solution%flux) + print *, " res size: ", size(solution%res) + end if + end function solution_create + + subroutine solution_initialize(this, initial_values) + class(solution_type), intent(inout) :: this + real(wp), intent(in), optional :: initial_values(:) + + integer(ip) :: i, idx + type(domain_type), pointer :: domain + + domain => this%domain + + if (present(initial_values)) then + ! 应用初始值到物理区域 + do i = domain%ist, domain%ied - 1 + idx = i - domain%ist + 1 + if (idx <= size(initial_values)) then + this%u(i) = initial_values(idx) + end if + end do + else + ! 默认为0 + this%u = 0.0_wp + end if + + ! 同步旧场 + call this%update_old_field() + + if (domain%config%verbose) then + print *, "[SOLUTION] Initialized" + end if + end subroutine solution_initialize + + subroutine solution_update_old_field(this) + class(solution_type), intent(inout) :: this + this%un = this%u + end subroutine solution_update_old_field + + subroutine solution_reset(this) + class(solution_type), intent(inout) :: this + this%u = 0.0_wp + this%un = 0.0_wp + this%q_face_left = 0.0_wp + this%q_face_right = 0.0_wp + this%flux = 0.0_wp + this%res = 0.0_wp + end subroutine solution_reset + + subroutine solution_print_info(this) + class(solution_type), intent(in) :: this + + print *, "=== Solution Information ===" + print *, "u size: ", size(this%u) + print *, "un size: ", size(this%un) + print *, "q_face_left size: ", size(this%q_face_left) + print *, "q_face_right size: ", size(this%q_face_right) + print *, "flux size: ", size(this%flux) + print *, "res size: ", size(this%res) + if (allocated(this%u)) then + print *, "u range: ", minval(this%u), " to ", maxval(this%u) + end if + print *, "============================" + end subroutine solution_print_info + +end module solution_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/src/manager/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02e/src/manager/CMakeLists.txt new file mode 100644 index 00000000..00c8bf49 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/src/manager/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/manager/CMakeLists.txt +message(STATUS "配置管理器模块...") + +# 创建管理器库 +add_library(manager STATIC + component_manager.f90 + component_factory.f90 +) + +# 明确依赖关系:管理器依赖所有其他模块 +target_link_libraries(manager + PRIVATE + core + infrastructure + reconstructor + flux +) + +# 设置模块输出目录 +set_target_properties(manager PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "管理器模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/src/manager/component_factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/02e/src/manager/component_factory.f90 new file mode 100644 index 00000000..114fedea --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/src/manager/component_factory.f90 @@ -0,0 +1,127 @@ +! src/manager/component_factory.f90 +module component_factory_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + use eno_reconstructor_module, only: eno_reconstructor, create_eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor, create_weno3_reconstructor + use rusanov_flux_module, only: rusanov_flux, create_rusanov_flux + + implicit none + private + public :: wp, create_reconstructor, create_flux_calculator + + ! 错误代码 + integer, parameter :: CM_SUCCESS = 0 + integer, parameter :: CM_ERROR_UNKNOWN_SCHEME = 1 + integer, parameter :: CM_ERROR_UNKNOWN_FLUX = 2 + integer, parameter :: CM_ERROR_INVALID_ORDER = 3 + +contains + + ! ==================== 重构器创建 ==================== + + function create_reconstructor(config, status) result(recon) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(reconstructor_base), allocatable :: recon + + character(len=20) :: scheme + integer :: order, error_code + + scheme = trim(adjustl(config%recon_scheme)) + order = config%spatial_order + + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating reconstructor: ", scheme, " order=", order + end if + + select case(scheme) + case('eno') + allocate(eno_reconstructor :: recon) + select type(recon) + type is(eno_reconstructor) + recon = create_eno_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case('weno3') + allocate(weno3_reconstructor :: recon) + select type(recon) + type is(weno3_reconstructor) + recon = create_weno3_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case default + error_code = CM_ERROR_UNKNOWN_SCHEME + if (config%verbose) then + print *, "[ERROR] Unknown reconstructor scheme: ", scheme + print *, " Available: eno, weno3" + end if + end select + + ! 检查阶数有效性 + if (error_code == CM_SUCCESS) then + if (order < 1) then + error_code = CM_ERROR_INVALID_ORDER + if (config%verbose) then + print *, "[ERROR] Invalid spatial order: ", order + end if + end if + end if + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Reconstructor creation failed" + end if + end function create_reconstructor + + ! ==================== 通量计算器创建 ==================== + + function create_flux_calculator(config, status) result(flux) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(flux_calculator_base), allocatable :: flux + + character(len=20) :: flux_type + integer :: error_code + + flux_type = trim(adjustl(config%flux_type)) + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating flux calculator: ", flux_type + end if + + select case(flux_type) + case('rusanov') + allocate(rusanov_flux :: flux) + select type(flux) + type is(rusanov_flux) + flux = create_rusanov_flux() + flux%wave_speed_default = config%wave_speed + end select + + case default + error_code = CM_ERROR_UNKNOWN_FLUX + if (config%verbose) then + print *, "[ERROR] Unknown flux type: ", flux_type + print *, " Available: rusanov" + end if + end select + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Flux calculator creation failed" + end if + end function create_flux_calculator + +end module component_factory_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/src/manager/component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/02e/src/manager/component_manager.f90 new file mode 100644 index 00000000..9e095c25 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/src/manager/component_manager.f90 @@ -0,0 +1,75 @@ +! src/manager/component_manager.f90 +module component_manager_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use component_factory_module, only: create_reconstructor, create_flux_calculator + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + implicit none + private + public :: wp, component_manager_info, validate_config + public :: create_reconstructor, create_flux_calculator + +contains + + ! ==================== 配置验证 ==================== + + function validate_config(config) result(is_valid) + type(cfd_config), intent(in) :: config + logical :: is_valid + + integer :: status + class(reconstructor_base), allocatable :: test_recon + class(flux_calculator_base), allocatable :: test_flux + + is_valid = .false. + + ! 测试创建重构器 + test_recon = create_reconstructor(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid reconstructor configuration" + end if + return + end if + + ! 测试创建通量计算器 + test_flux = create_flux_calculator(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid flux configuration" + end if + return + end if + + ! 清理测试组件 + if (allocated(test_recon)) deallocate(test_recon) + if (allocated(test_flux)) deallocate(test_flux) + + is_valid = .true. + + if (config%verbose) then + print *, "[CONFIG VALIDATION] Configuration is valid" + end if + end function validate_config + + ! ==================== 信息显示 ==================== + + subroutine component_manager_info() + print *, "=== Component Manager ===" + print *, "Available reconstructors:" + print *, " - eno (orders: 1-7)" + print *, " - weno3 (order: 3)" + print *, "" + print *, "Available flux calculators:" + print *, " - rusanov" + print *, "" + print *, "Features:" + print *, " - Configuration validation" + print *, " - Component creation from config" + print *, " - Error handling with status codes" + print *, "=========================" + end subroutine component_manager_info + +end module component_manager_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02e/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02e/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..3fde7746 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,28 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux STATIC + base.f90 + rusanov.f90 +) + +#target_link_libraries(flux PRIVATE core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 明确设置不使用 /Qm64 选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + set_target_properties(flux PROPERTIES + Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /Qm64" # 明确设置,避免继承问题 + ) +endif() + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/02e/src/numerics/flux/base.f90 new file mode 100644 index 00000000..7080a7ae --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/src/numerics/flux/base.f90 @@ -0,0 +1,30 @@ +!src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + procedure :: print_basic_info => flux_print_basic ! 添加辅助方法 + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + + subroutine flux_print_basic(this) + class(flux_calculator_base), intent(in) :: this + print *, " Name: ", trim(this%name) + end subroutine flux_print_basic + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/02e/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..daa9e3bb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/src/numerics/flux/rusanov.f90 @@ -0,0 +1,41 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux, create_rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + + ! 构造函数接口 + interface rusanov_flux + module procedure create_rusanov_flux + end interface + +contains + + ! 构造函数 + type(rusanov_flux) function create_rusanov_flux() result(this) + this%name = "Rusanov" + this%wave_speed_default = 1.0_real64 + end function create_rusanov_flux + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Flux calculator information:" + call this%print_basic_info() + + ! 添加Rusanov特有信息 + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02e/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..5e4b938d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,22 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor STATIC + base.f90 + eno.f90 + weno3.f90 +) + +target_link_libraries(reconstructor PRIVATE core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/02e/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..53798d02 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/src/numerics/reconstructor/base.f90 @@ -0,0 +1,33 @@ +!src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + procedure :: print_basic_info => reconstructor_print_basic ! 添加一个辅助方法 + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + + subroutine reconstructor_print_basic(this) + class(reconstructor_base), intent(in) :: this + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_print_basic + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/02e/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..f973e8b3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor, create_eno_reconstructor ! ← 添加这个 + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + + ! 构造函数接口 + interface eno_reconstructor + module procedure create_eno_reconstructor + end interface + +contains + + ! 构造函数 + type(eno_reconstructor) function create_eno_reconstructor() result(this) + this%name = "ENO" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_eno_reconstructor + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加ENO特有信息 + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/02e/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..d5b7a747 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno3_reconstructor, create_weno3_reconstructor + + type, extends(reconstructor_base) :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + + ! 构造函数接口 + interface weno3_reconstructor + module procedure create_weno3_reconstructor + end interface + +contains + + ! 构造函数 + type(weno3_reconstructor) function create_weno3_reconstructor() result(this) + this%name = "WENO3" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_weno3_reconstructor + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO3特有信息 + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02e/tests/CMakeLists.txt new file mode 100644 index 00000000..7b949064 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/tests/CMakeLists.txt @@ -0,0 +1,71 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +# +#message(STATUS "CMAKE_Fortran_MODULE_DIRECTORY=${CMAKE_Fortran_MODULE_DIRECTORY}") +# +add_executable(test_minimal_simple test_minimal_simple.f90) +target_link_libraries(test_minimal_simple + PRIVATE + core # 必须链接core库 + infrastructure +) + + +add_executable(test_simple_link test_simple_link.f90) +target_link_libraries(test_simple_link + PRIVATE + reconstructor + flux +) + + +add_executable(test_factory_simple test_factory_simple.f90) +target_link_libraries(test_factory_simple + PRIVATE + core + infrastructure + reconstructor + flux +) + +# 更新测试链接 +#add_executable(test_component_manager test_component_manager.f90) +# +#target_link_libraries(test_component_manager +# PRIVATE +# manager # ← 链接到新的管理器库 +# infrastructure +#) +# +#add_executable(test_architecture test_architecture.f90) +#target_link_libraries(test_architecture +# PRIVATE +# core +# infrastructure +# manager +#) + +#add_executable(test_solver_framework test_solver_framework.f90) +#target_link_libraries(test_solver_framework +# PRIVATE +# core +# infrastructure +# manager +#) +# +#add_executable(test_cfd_architecture test_cfd_architecture.f90) +#target_link_libraries(test_cfd_architecture +# PRIVATE +# base +# core +# infrastructure +#) + + +add_executable(test_basic_only test_basic_only.f90) +target_link_libraries(test_basic_only + PRIVATE + infrastructure + core +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/tests/test_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/02e/tests/test_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/tests/test_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/tests/test_basic_only.f90 b/example/1d-linear-convection/weno3/fortran/registry/02e/tests/test_basic_only.f90 new file mode 100644 index 00000000..20901ddc --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/tests/test_basic_only.f90 @@ -0,0 +1,56 @@ +! tests/test_basic_only.f90 +program test_basic_only + ! 只测试最基本的功能,不依赖复杂模块 + use config_module, only: cfd_config, config_print, wp + use mesh_module, only: mesh_type + use registry_module, only: registry_init, registry_cleanup, & + register_component_simple, list_components + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "=== BASIC TEST - Minimal Functionality ===" + print *, "" + + ! 测试1: 配置 + print *, "1. Testing configuration..." + print *, "----------------------------" + call config_print(config) + print *, "" + + ! 测试2: 网格 + print *, "2. Testing mesh..." + print *, "------------------" + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=5) + print *, "Mesh initialized:" + print *, " Cells: ", mesh%ncells + print *, " Nodes: ", mesh%nnodes + print *, " dx: ", mesh%dx + print *, "" + + ! 测试3: 注册系统 + print *, "3. Testing registry..." + print *, "----------------------" + + call registry_init() + + ! 注册组件(使用简化版本) + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! 列出组件 + call list_components() + print *, "" + + ! 清理 + call registry_cleanup() + + print *, "=== TEST PASSED ===" + print *, "✓ Configuration works" + print *, "✓ Mesh works" + print *, "✓ Registry works" + +end program test_basic_only \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/tests/test_cfd_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/02e/tests/test_cfd_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/tests/test_cfd_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/tests/test_component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/02e/tests/test_component_manager.f90 new file mode 100644 index 00000000..f60c3505 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/tests/test_component_manager.f90 @@ -0,0 +1,111 @@ +! tests/test_component_manager.f90 +program test_component_manager + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use component_manager_module, only: create_reconstructor, create_flux_calculator + use component_manager_module, only: component_manager_info, validate_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + implicit none + + type(cfd_config) :: config + class(reconstructor_base), allocatable :: recon + class(flux_calculator_base), allocatable :: flux + integer :: status + logical :: is_valid + + print *, "=== Component Manager Test ===" + print *, "" + + ! 显示组件管理器信息 + call component_manager_info() + print *, "" + + ! 测试1: 基本配置 + print *, "1. Testing basic ENO3 + Rusanov configuration..." + print *, "-----------------------------------------------" + + config%verbose = .true. + call config_print(config) + + ! 配置ENO3重构 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + call config_print(config) + print *, "" + + ! 验证配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Configuration is valid" + else + print *, "[ERROR] Configuration is invalid" + end if + print *, "" + + ! 测试2: 创建组件 + print *, "2. Testing component creation..." + print *, "--------------------------------" + + ! 创建重构器(带状态检查) + recon = create_reconstructor(config, status) + if (status == 0) then + print *, "[OK] Reconstructor created successfully" + call recon%info() + else + print *, "[ERROR] Failed to create reconstructor, code:", status + end if + print *, "" + + ! 创建通量计算器 + flux = create_flux_calculator(config, status) + if (status == 0) then + print *, "[OK] Flux calculator created successfully" + call flux%info() + else + print *, "[ERROR] Failed to create flux calculator, code:", status + end if + print *, "" + + ! 测试3: WENO3重构测试 + print *, "3. Testing WENO3 configuration..." + print *, "---------------------------------" + + call config_with_reconstruction(config, "weno3", 3) + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] WENO3 configuration is valid" + + ! 创建WENO3重构器 + recon = create_reconstructor(config) + call recon%info() + else + print *, "[ERROR] WENO3 configuration is invalid" + end if + print *, "" + + ! 测试4: 错误配置测试 + print *, "4. Testing invalid configuration..." + print *, "-----------------------------------" + + config%recon_scheme = "unknown_scheme" + config%flux_type = "unknown_flux" + + is_valid = validate_config(config) + if (.not. is_valid) then + print *, "[OK] Invalid configuration correctly rejected" + else + print *, "[ERROR] Invalid configuration should have been rejected" + end if + + ! 清理 + if (allocated(recon)) deallocate(recon) + if (allocated(flux)) deallocate(flux) + + print *, "" + print *, "=== Component manager test completed successfully ===" + +end program test_component_manager \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/tests/test_factory_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/02e/tests/test_factory_simple.f90 new file mode 100644 index 00000000..db65da7c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/tests/test_factory_simple.f90 @@ -0,0 +1,58 @@ +! tests/test_factory_simple.f90 (修复版) +program test_factory_simple + use base_modules, only: wp ! ← 添加这行 + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Basic systems + print *, "1. Testing basic systems..." + print *, "-----------------------------" + call config_print(config) + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Creating reconstructors + print *, "2. Testing reconstructors..." + print *, "------------------------------" + + ! 创建并测试ENO重构器 + print *, "Creating ENO reconstructor..." + eno = eno_reconstructor() ! 使用构造函数 + call eno%info() ! 必须调用info方法 + + print *, "" + print *, "Creating WENO3 reconstructor..." + weno3 = weno3_reconstructor() ! 使用构造函数 + call weno3%info() ! 必须调用info方法 + print *, "" + + ! Test 3: Creating flux calculator + print *, "3. Testing flux calculator..." + print *, "-------------------------------" + + print *, "Creating Rusanov flux calculator..." + rusanov = rusanov_flux() ! 使用构造函数 + call rusanov%info() ! 必须调用info方法 + print *, "" + + print *, "=== Factory pattern simple test completed successfully ===" + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/tests/test_minimal_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/02e/tests/test_minimal_simple.f90 new file mode 100644 index 00000000..ec03ccf8 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/tests/test_minimal_simple.f90 @@ -0,0 +1,87 @@ +! tests/test_minimal_simple.f90 +program test_minimal_simple + use base_modules, only: wp ! ← 添加这行 + use registry_module + use config_module + use mesh_module + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Simple Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call registry_init() + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call list_components() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + + if (has_component_simple("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component_simple("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components (simple version) + print *, "5. Testing registry functionality" + print *, "---------------------------------" + print *, "Registry initialized: ", registry_is_initialized() + print *, "Number of components: ", registry_get_size() + print *, "" + + ! Cleanup + call registry_cleanup() + + print *, "=== Simple test completed successfully ===" + +end program test_minimal_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/tests/test_simple_link.f90 b/example/1d-linear-convection/weno3/fortran/registry/02e/tests/test_simple_link.f90 new file mode 100644 index 00000000..71cc614e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/tests/test_simple_link.f90 @@ -0,0 +1,78 @@ +! tests/test_simple_link.f90 +program test_simple_link + use base_modules, only: wp ! ← 添加这行 + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call registry_init() + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call list_components() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component_simple("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component_simple("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Cleanup + call registry_cleanup() + + print *, "=== Minimal test completed successfully ===" + +end program test_simple_link \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02e/tests/test_solver_framework.f90 b/example/1d-linear-convection/weno3/fortran/registry/02e/tests/test_solver_framework.f90 new file mode 100644 index 00000000..6754323d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02e/tests/test_solver_framework.f90 @@ -0,0 +1,91 @@ +! tests/test_solver_framework.f90 +program test_solver_framework + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use mesh_module, only: mesh_type + use solver_module, only: cfd_solver, solver_create, solver_run, solver_cleanup + use solver_module, only: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, & + SOLVER_COMPLETED, SOLVER_ERROR + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(cfd_solver) :: solver + + print *, "=== 求解器框架测试 ===" + print *, "" + + ! 测试1: 基本创建 + print *, "1. 测试求解器创建..." + print *, "----------------------" + + ! 创建配置 + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.0_real64 + config%dt = 0.01_real64 + + call config_print(config) + print *, "" + + ! 创建网格 + call mesh%init(xmin=0.0_real64, xmax=2.0_real64, ncells=20) + call mesh%print_info() + print *, "" + + ! 创建求解器 + solver = solver_create(config, mesh) + print *, "✓ 求解器创建成功" + print *, " 状态: ", solver%get_state() + print *, "" + + ! 测试2: 求解器初始化 + print *, "2. 测试求解器初始化..." + print *, "------------------------" + + call solver%initialize() + print *, "✓ 求解器初始化完成" + print *, " 状态: ", solver%get_state() + print *, " 错误信息: '", trim(solver%get_error()), "'" + print *, "" + + ! 测试3: 简单运行 + print *, "3. 测试求解器运行..." + print *, "----------------------" + + call solver_run(solver, 0.05_real64) ! 运行到0.05秒 + print *, "✓ 求解器运行完成" + print *, " 最终状态: ", solver%get_state() + print *, "" + + ! 测试4: 清理 + print *, "4. 测试求解器清理..." + print *, "----------------------" + + call solver_cleanup(solver) + print *, "✓ 求解器清理完成" + print *, " 状态: ", solver%get_state() + print *, "" + + ! 测试5: 错误处理 + print *, "5. 测试错误处理..." + print *, "-------------------" + + ! 尝试重复初始化 + call solver%initialize() + print *, " 重复初始化状态: ", solver%get_state() + print *, " 错误信息: '", trim(solver%get_error()), "'" + + call solver_cleanup(solver) + print *, "" + + print *, "=== 框架测试总结 ===" + print *, "✓ 求解器创建/初始化/运行/清理流程验证完成" + print *, "✓ 状态管理正常工作" + print *, "✓ 错误处理机制就绪" + print *, "" + print *, "下一步: 添加实际数值计算功能" + +end program test_solver_framework \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02f/CMakeLists.txt new file mode 100644 index 00000000..ef66d584 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/README.md b/example/1d-linear-convection/weno3/fortran/registry/02f/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/02f/scripts/build.bat new file mode 100644 index 00000000..6fd6dc03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/scripts/build.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Project Builder +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python build script with full Intel environment support... +echo. + +python build.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Build failed + pause + exit /b 1 +) + +echo. +echo [INFO] Build completed successfully! +echo. +echo [INFO] To run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/02f/scripts/build.py new file mode 100644 index 00000000..3bf6d537 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/scripts/build.py @@ -0,0 +1,629 @@ +#!/usr/bin/env python3 +""" +Fortran CFD Project Builder - 完整Python解决方案 +在Python内部处理Intel oneAPI环境配置 +""" + +import os +import sys +import subprocess +import shutil +import argparse +import time +import platform +import tempfile +from pathlib import Path + +class IntelEnvironment: + """Intel oneAPI环境管理器""" + + def __init__(self): + self.setvars_path = None + self.env_vars = {} + + def find_setvars(self): + """查找setvars.bat文件""" + possible_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + r"C:\Program Files (x86)\Intel\oneAPI\compiler\latest\env\vars.bat", + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\setvars.bat"), + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\compiler\latest\env\vars.bat"), + ] + + for path in possible_paths: + if os.path.exists(path): + self.setvars_path = path + return True + + return False + + def setup_environment(self): + """设置Intel环境""" + if not self.find_setvars(): + return False + + try: + # 创建临时的批处理文件来捕获环境变量 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + f.write(f'@echo off\n') + f.write(f'call "{self.setvars_path}" >nul 2>&1\n') + f.write(f'set\n') # 输出所有环境变量 + temp_bat = f.name + + # 运行批处理文件并捕获输出 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + shell=True + ) + + # 解析环境变量 + for line in result.stdout.split('\n'): + line = line.strip() + if '=' in line: + key, value = line.split('=', 1) + self.env_vars[key.strip()] = value.strip() + + # 清理临时文件 + os.unlink(temp_bat) + + # 更新当前进程的环境变量 + os.environ.update(self.env_vars) + + return True + + except Exception as e: + print(f"设置Intel环境失败: {e}") + return False + + def get_compiler_info(self): + """获取编译器信息""" + info = {} + + # 检查ifx编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + env={**os.environ, **self.env_vars} if self.env_vars else os.environ + ) + + if result.returncode == 0: + for line in result.stdout.split('\n'): + if 'Version' in line or '版本' in line: + info['ifx_version'] = line.strip() + break + except: + pass + + # 检查环境变量 + info['ifx_root'] = self.env_vars.get('IFX_ROOT', '') + info['compiler_root'] = self.env_vars.get('ONEAPI_ROOT', '') + + return info + +class BuildSystem: + """构建系统主类""" + + def __init__(self): + self.project_root = Path(__file__).parent.parent + self.build_dir = self.project_root / "build" + self.intel_env = IntelEnvironment() + + # 设置控制台编码 + if sys.platform == "win32": + try: + import ctypes + # 设置控制台输出为UTF-8 + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + except: + pass + + def print_header(self, text): + """打印标题""" + print(f"\n{'='*70}") + print(f" {text}") + print(f"{'='*70}\n") + + def print_step(self, step, total, message): + """打印步骤""" + print(f"[{step}/{total}] {message}...") + + def print_success(self, message): + """打印成功""" + print(f"\033[92m✓ {message}\033[0m") + + def print_error(self, message): + """打印错误""" + print(f"\033[91m✗ {message}\033[0m") + + def print_warning(self, message): + """打印警告""" + print(f"\033[93m! {message}\033[0m") + + def print_info(self, message): + """打印信息""" + print(f"\033[94mℹ {message}\033[0m") + + def check_prerequisites(self): + """检查前提条件""" + self.print_step(1, 6, "检查前提条件") + + # 检查Python版本 + python_version = sys.version.split()[0] + self.print_info(f"Python版本: {python_version}") + + # 检查平台 + self.print_info(f"平台: {platform.system()} {platform.release()}") + self.print_info(f"处理器核心数: {os.cpu_count()}") + + # 检查CMake + try: + result = subprocess.run( + ["cmake", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + version_line = result.stdout.split('\n')[0] + self.print_success(f"CMake: {version_line}") + else: + self.print_error("CMake未找到") + return False + except FileNotFoundError: + self.print_error("CMake未安装") + return False + + return True + + def setup_intel_environment(self, args): + """设置Intel环境""" + self.print_step(2, 6, "配置Intel oneAPI环境") + + if not self.intel_env.find_setvars(): + self.print_warning("未找到Intel oneAPI setvars.bat") + self.print_info("将尝试使用系统环境中的编译器") + + # 检查是否能直接访问编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + self.print_success("Intel编译器在系统PATH中找到") + return True + else: + self.print_warning("Intel编译器未在PATH中找到") + except: + self.print_warning("无法访问Intel编译器") + + return True # 继续,让CMake自己找编译器 + + # 设置环境 + if self.intel_env.setup_environment(): + compiler_info = self.intel_env.get_compiler_info() + + if compiler_info.get('ifx_version'): + self.print_success(f"Intel Fortran编译器: {compiler_info['ifx_version']}") + elif compiler_info.get('ifx_root'): + self.print_success(f"Intel编译器路径: {compiler_info['ifx_root']}") + else: + self.print_success("Intel oneAPI环境配置完成") + + return True + else: + self.print_warning("Intel环境配置失败,将继续使用系统环境") + return True + + def clean_build_directory(self, args): + """清理构建目录""" + if args.clean and self.build_dir.exists(): + self.print_info("清理构建目录...") + try: + shutil.rmtree(self.build_dir) + self.print_success("构建目录已清理") + except Exception as e: + self.print_error(f"清理失败: {e}") + if not args.force: + return False + return True + + def run_command(self, cmd, cwd=None, check=True, env=None): + """运行命令""" + if isinstance(cmd, list): + cmd_str = ' '.join(str(c) for c in cmd if c) + else: + cmd_str = str(cmd) + + print(f" \033[96m$\033[0m {cmd_str}") + + try: + # 合并环境变量 + exec_env = os.environ.copy() + if env: + exec_env.update(env) + if self.intel_env.env_vars: + exec_env.update(self.intel_env.env_vars) + + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=False, + env=exec_env + ) + + # 处理输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or '完成' in line or '生成' in line: + print(f" \033[92m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + if check and result.returncode != 0: + self.print_error(f"命令执行失败,退出码: {result.returncode}") + return False + + return True + + except Exception as e: + self.print_error(f"命令执行异常: {e}") + return False + + def configure_cmake(self, args): + """配置CMake""" + self.print_step(3, 6, "配置CMake项目") + + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ] + + if args.compiler == "ifx": + cmake_cmd.extend(["-T", "fortran=ifx"]) + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + success = self.run_command(cmake_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("CMake配置完成") + else: + self.print_error("CMake配置失败") + + return success + + def build_project(self, args): + """构建项目""" + self.print_step(4, 6, "构建项目") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + success = self.run_command(build_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("项目构建完成") + else: + self.print_error("构建失败") + + return success + + def run_tests_with_environment(self, test_exe): + """运行单个测试,确保有Intel环境""" + try: + # 创建临时的批处理文件来运行测试 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'"{test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'"{test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + return result + + except Exception as e: + print(f"运行测试失败: {e}") + return None + + def run_tests(self, args): + """运行测试""" + self.print_step(5, 6, "运行测试") + + # 查找测试可执行文件 + test_dir = self.build_dir / "bin" / args.build_type + if not test_dir.exists(): + test_dir = self.build_dir / "bin" + if not test_dir.exists(): + test_dir = self.build_dir + + test_files = list(test_dir.glob("test_*.exe")) + + if not test_files: + self.print_warning("未找到测试程序") + return True + + all_passed = True + + for test_exe in sorted(test_files): + test_name = test_exe.stem + self.print_info(f"运行测试: {test_name}") + print(f" {'-'*50}") + + # 运行测试 + result = self.run_tests_with_environment(str(test_exe)) + + if result is None: + self.print_error(f" {test_name} 运行失败") + all_passed = False + continue + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + print(f" {line}") + + if result.returncode == 0: + self.print_success(f" {test_name} 通过") + else: + self.print_error(f" {test_name} 失败 (退出码: {result.returncode})") + all_passed = False + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + print() # 空行 + + return all_passed + + def create_test_runner(self, args): + """创建独立的测试运行器""" + self.print_step(6, 6, "创建测试运行器") + + runner_path = self.build_dir / "run_tests.bat" + + content = f'''@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Test Runner +echo ======================================== +echo. + +REM Setup Intel oneAPI environment +set "SETVARS_PATH={self.intel_env.setvars_path or ''}" +if exist "%SETVARS_PATH%" ( + call "%SETVARS_PATH%" >nul + echo [INFO] Intel environment configured +) else ( + echo [WARNING] Intel environment not found + echo [WARNING] Tests may fail without runtime libraries +) + +echo. + +REM Run all test executables +set "TEST_COUNT=0" +set "PASS_COUNT=0" + +for %%f in ("bin\\{args.build_type}\\test_*.exe") do ( + set /a TEST_COUNT+=1 + echo [TEST %%f] + echo {'-'*50} + + %%f + if errorlevel 1 ( + echo [FAILED] %%f + ) else ( + echo [PASSED] %%f + set /a PASS_COUNT+=1 + ) + echo. +) + +echo ======================================== +echo Tests: %PASS_COUNT%/%TEST_COUNT% passed +if %PASS_COUNT% equ %TEST_COUNT% ( + echo [SUCCESS] All tests passed! +) else ( + echo [FAILURE] Some tests failed +) +echo ======================================== + +pause +''' + + with open(runner_path, 'w', encoding='utf-8') as f: + f.write(content) + + self.print_success(f"测试运行器已创建: {runner_path}") + self.print_info(f"使用方法: cd build && run_tests.bat") + + return runner_path + + def generate_report(self, args, build_time, tests_passed): + """生成构建报告""" + self.print_header("构建完成") + + print(f"项目: {self.project_root.name}") + print(f"构建类型: {args.build_type}") + print(f"编译器: {args.compiler}") + print(f"并行作业: {args.jobs}") + print(f"总耗时: {build_time:.1f}秒") + print(f"测试结果: {'全部通过' if tests_passed else '有失败'}") + + # 显示生成的可执行文件 + bin_dir = self.build_dir / "bin" / args.build_type + if bin_dir.exists(): + print(f"\n生成的可执行文件:") + for exe in sorted(bin_dir.glob("*.exe")): + size_mb = exe.stat().st_size / (1024 * 1024) + print(f" • {exe.name} ({size_mb:.2f} MB)") + + # 显示测试运行器信息 + runner_path = self.build_dir / "run_tests.bat" + if runner_path.exists(): + print(f"\n独立测试运行器:") + print(f" • {runner_path.name}") + print(f" 在Intel oneAPI环境中运行所有测试") + + def run(self): + """运行构建系统""" + parser = argparse.ArgumentParser( + description="Fortran CFD项目构建工具", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认构建 + %(prog)s --clean # 清理后构建 + %(prog)s --build-type Release # Release构建 + %(prog)s --no-tests # 只构建,不运行测试 + %(prog)s -j8 --verbose # 8线程并行构建,详细输出 + """ + ) + + parser.add_argument("--build-type", choices=["Debug", "Release"], + default="Debug", help="构建类型") + parser.add_argument("--compiler", choices=["ifx", "ifort"], + default="ifx", help="Fortran编译器") + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-tests", action="store_true", + help="跳过测试") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + + args = parser.parse_args() + + # 开始构建 + start_time = time.time() + + self.print_header("Fortran CFD 项目构建系统") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 检查前提条件 + if not self.check_prerequisites(): + if not args.force: + return 1 + + # 2. 设置Intel环境 + if not self.setup_intel_environment(args): + if not args.force: + return 1 + + # 3. 清理目录 + if not self.clean_build_directory(args): + if not args.force: + return 1 + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 4. 配置CMake + if not self.configure_cmake(args): + if not args.force: + return 1 + + # 5. 构建项目 + if not self.build_project(args): + if not args.force: + return 1 + + # 6. 运行测试和创建测试运行器 + tests_passed = True + if not args.no_tests: + tests_passed = self.run_tests(args) + + # 创建测试运行器 + self.create_test_runner(args) # 传递 args 参数 + + # 7. 生成报告 + build_time = time.time() - start_time + self.generate_report(args, build_time, tests_passed) + + return 0 if tests_passed else 1 + + except KeyboardInterrupt: + self.print_error("\n构建被用户中断") + return 1 + except Exception as e: + self.print_error(f"构建过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + builder = BuildSystem() + return builder.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/02f/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02f/src/CMakeLists.txt new file mode 100644 index 00000000..d8cbd5f3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/src/CMakeLists.txt @@ -0,0 +1,16 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(base) +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) +add_subdirectory(manager) + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/src/base/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02f/src/base/CMakeLists.txt new file mode 100644 index 00000000..d537affd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/src/base/CMakeLists.txt @@ -0,0 +1,15 @@ +# src/base/CMakeLists.txt +message(STATUS "Configuring base module...") + +add_library(base STATIC + modules.f90 +) + +set_target_properties(base PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Base module configured") + +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/src/base/modules.f90 b/example/1d-linear-convection/weno3/fortran/registry/02f/src/base/modules.f90 new file mode 100644 index 00000000..99ef3337 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/src/base/modules.f90 @@ -0,0 +1,42 @@ +! src/base/modules.f90 +module base_modules + use, intrinsic :: iso_fortran_env, only: real64, int32 + + implicit none + + ! 公开所有基本类型 + public :: wp, ip, string_len, max_name_len + public :: cfd_config_base, component_info + + ! 工作精度类型 + integer, parameter :: wp = real64 + integer, parameter :: ip = int32 + + ! 字符串长度常量(使用数值常量) + integer, parameter :: string_len = 100 + integer, parameter :: max_name_len = 32 + + ! 基础配置类型 + type :: cfd_config_base + character(len=max_name_len) :: ic_type = "step" + character(len=max_name_len) :: recon_scheme = "eno" + character(len=max_name_len) :: flux_type = "rusanov" + integer(ip) :: rk_order = 1 + real(wp) :: wave_speed = 1.0_wp + real(wp) :: final_time = 0.625_wp + real(wp) :: dt = 0.025_wp + character(len=max_name_len) :: boundary_type = "periodic" + integer(ip) :: spatial_order = 2 + character(len=max_name_len) :: equation_type = "linear_advection" + character(len=max_name_len) :: problem_type = "linear_advection" + logical :: verbose = .true. + end type cfd_config_base + + ! 组件信息类型 + type :: component_info + character(len=max_name_len) :: category = "" + character(len=max_name_len) :: name = "" + integer(ip) :: order = 0 + end type component_info + +end module base_modules \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02f/src/core/CMakeLists.txt new file mode 100644 index 00000000..d8b8df06 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/src/core/CMakeLists.txt @@ -0,0 +1,14 @@ +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") + +add_library(core STATIC + registry.f90 +) + +target_link_libraries(core PRIVATE base) + +set_target_properties(core PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Core module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/src/core/factory_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/02f/src/core/factory_base.f90 new file mode 100644 index 00000000..302418a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/src/core/factory_base.f90 @@ -0,0 +1,57 @@ +! src/core/factory_base.f90 +module factory_base_module + use base_modules, only: wp, ip + use registry_module, only: create_component, has_component + + implicit none + private + public :: wp, ip, factory_base, factory_create + + ! 工厂基类 + type :: factory_base + character(len=max_name_length) :: category = "" + contains + procedure :: create => factory_base_create + procedure :: get_available => factory_base_get_available + end type factory_base + + ! 便捷函数类型 + abstract interface + function factory_function_interface(category, name) result(instance) + import :: wp + character(len=*), intent(in) :: category, name + class(*), allocatable :: instance + end function factory_function_interface + end interface + +contains + + ! 创建工厂实例 + function factory_create(category) result(factory) + character(len=*), intent(in) :: category + type(factory_base) :: factory + factory%category = trim(category) + end function factory_create + + ! 工厂创建方法 + function factory_base_create(this, name) result(instance) + class(factory_base), intent(in) :: this + character(len=*), intent(in) :: name + class(*), allocatable :: instance + + instance = create_component(this%category, name) + end function factory_base_create + + ! 获取可用组件列表(简化版) + subroutine factory_base_get_available(this, names, count) + class(factory_base), intent(in) :: this + character(len=*), allocatable, intent(out) :: names(:) + integer(ip), intent(out) :: count + + ! 这里需要实现从注册表获取列表的逻辑 + ! 暂时返回空列表 + count = 0 + allocate(character(len=max_name_length) :: names(0)) + end subroutine factory_base_get_available + +end module factory_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/02f/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..a960167c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/02f/src/core/registry.f90 new file mode 100644 index 00000000..acc63edb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/src/core/registry.f90 @@ -0,0 +1,203 @@ +! src/core/registry.f90 +module registry_module + use base_modules, only: wp, ip, max_name_len, component_info + + implicit none + private + + ! 明确公开所有需要的接口 + public :: wp, ip ! 类型参数 + public :: component_info ! 类型 + public :: registry_init, registry_cleanup ! 初始化/清理 + public :: register_component_simple ! 注册组件 + public :: has_component_simple ! 检查组件 + public :: list_components ! 列出组件 + public :: registry_is_initialized ! 检查初始化状态 ← 新增 + public :: registry_get_size ! 获取大小 ← 新增 + + ! 全局注册表 + type :: component_registry + type(component_info), allocatable :: components(:) + integer(ip) :: count = 0 + integer(ip) :: capacity = 100 + logical :: initialized = .false. + logical :: verbose = .true. + end type component_registry + + type(component_registry) :: registry + +contains + + ! ==================== 公共API ==================== + + subroutine registry_init(verbose) + logical, optional, intent(in) :: verbose + + if (registry%initialized) then + if (registry%verbose) then + print *, "[REGISTRY] Already initialized" + end if + return + end if + + if (present(verbose)) then + registry%verbose = verbose + end if + + allocate(registry%components(registry%capacity)) + registry%initialized = .true. + + if (registry%verbose) then + print *, "[REGISTRY] Initialized with capacity:", registry%capacity + end if + end subroutine registry_init + + subroutine registry_cleanup() + if (allocated(registry%components)) then + deallocate(registry%components) + end if + registry%initialized = .false. + registry%count = 0 + + if (registry%verbose) then + print *, "[REGISTRY] Cleaned up" + end if + end subroutine registry_cleanup + + subroutine register_component_simple(category, name, order) + character(len=*), intent(in) :: category, name + integer(ip), optional, intent(in) :: order + + integer(ip) :: i + type(component_info) :: info + + if (.not. registry%initialized) then + call registry_init() + end if + + ! 检查是否已存在 + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + if (registry%verbose) then + print *, "[WARN] Overwriting component: ", trim(category), ".", trim(name) + end if + + ! 更新 + if (present(order)) then + registry%components(i)%order = order + else + registry%components(i)%order = 0 + end if + return + end if + end do + + ! 扩展数组 + if (registry%count >= registry%capacity) then + call expand_registry() + end if + + ! 添加新组件 + registry%count = registry%count + 1 + + info%category = trim(category) + info%name = trim(name) + info%order = 0 + if (present(order)) then + info%order = order + end if + + registry%components(registry%count) = info + + if (registry%verbose) then + print *, "[OK] Registered simple: ", trim(category), ".", trim(name) + end if + end subroutine register_component_simple + + logical function has_component_simple(category, name) + character(len=*), intent(in) :: category, name + + integer(ip) :: i + + has_component_simple = .false. + + if (.not. registry%initialized) return + + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + has_component_simple = .true. + return + end if + end do + end function has_component_simple + + subroutine list_components(category) + character(len=*), optional, intent(in) :: category + + integer(ip) :: i, count + + if (.not. registry%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + if (registry%count == 0) then + print *, "[INFO] No components registered" + return + end if + + count = 0 + print *, "=== Registry Contents ===" + do i = 1, registry%count + if (.not. present(category) .or. & + trim(registry%components(i)%category) == trim(category)) then + call print_component_info(registry%components(i)) + count = count + 1 + end if + end do + + print *, "Total:", count, "components" + print *, "==========================" + end subroutine list_components + + ! ==================== 新增函数 ==================== + + logical function registry_is_initialized() + ! 检查注册表是否已初始化 + registry_is_initialized = registry%initialized + end function registry_is_initialized + + integer(ip) function registry_get_size() + ! 获取注册表中的组件数量 + registry_get_size = registry%count + end function registry_get_size + + ! ==================== 内部辅助函数 ==================== + + subroutine expand_registry() + type(component_info), allocatable :: temp(:) + + registry%capacity = registry%capacity * 2 + allocate(temp(registry%capacity)) + temp(1:registry%count) = registry%components(1:registry%count) + call move_alloc(temp, registry%components) + + if (registry%verbose) then + print *, "[INFO] Registry expanded to capacity:", registry%capacity + end if + end subroutine expand_registry + + subroutine print_component_info(info) + type(component_info), intent(in) :: info + + if (info%order > 0) then + print *, " [", trim(info%category), ".", trim(info%name), & + " (order:", info%order, ")]" + else + print *, " [", trim(info%category), ".", trim(info%name), "]" + end if + end subroutine print_component_info + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02f/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..70cbbd2f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/src/infrastructure/CMakeLists.txt @@ -0,0 +1,17 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "Configuring infrastructure module...") + +add_library(infrastructure STATIC + config.f90 + mesh.f90 + domain.f90 # 新增 + solution.f90 # 新增 +) + +target_link_libraries(infrastructure PRIVATE base) + +set_target_properties(infrastructure PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Infrastructure module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/02f/src/infrastructure/config.f90 new file mode 100644 index 00000000..719e14d2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/src/infrastructure/config.f90 @@ -0,0 +1,64 @@ +! src/infrastructure/config.f90 +module config_module + use base_modules, only: wp, ip, max_name_len, cfd_config_base + + implicit none + public :: wp, ip, cfd_config, config_print, config_with_reconstruction + + ! 扩展配置类型 + type, extends(cfd_config_base) :: cfd_config + real(wp) :: left_boundary_value = 1.0_wp + real(wp) :: right_boundary_value = 2.0_wp + real(wp) :: domain_length = 2.0_wp + end type cfd_config + +contains + + subroutine config_print(cfg) + type(cfd_config), intent(in) :: cfg + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(cfg%ic_type) + print *, "Reconstruction: ", trim(cfg%recon_scheme), " (order:", cfg%spatial_order, ")" + print *, "Flux type: ", trim(cfg%flux_type) + print *, "Time integration: RK", cfg%rk_order + print *, "Wave speed: ", cfg%wave_speed + print *, "Final time: ", cfg%final_time + print *, "Time step: ", cfg%dt + print *, "Boundary: ", trim(cfg%boundary_type) + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(cfg, scheme, order) + type(cfd_config), intent(inout) :: cfg + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + integer :: i + + ! 转换为小写 + cfg%recon_scheme = scheme + do i = 1, len_trim(cfg%recon_scheme) + if (cfg%recon_scheme(i:i) >= 'A' .and. cfg%recon_scheme(i:i) <= 'Z') then + cfg%recon_scheme(i:i) = char(ichar(cfg%recon_scheme(i:i)) + 32) + end if + end do + + ! 设置阶数 + if (present(order)) then + cfg%spatial_order = order + else + if (index(cfg%recon_scheme, 'weno') > 0) then + cfg%spatial_order = 5 + else if (trim(cfg%recon_scheme) == 'eno') then + cfg%spatial_order = 3 + end if + end if + + if (cfg%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(cfg%recon_scheme), & + " Order: ", cfg%spatial_order + end if + end subroutine config_with_reconstruction + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/src/infrastructure/domain.f90 b/example/1d-linear-convection/weno3/fortran/registry/02f/src/infrastructure/domain.f90 new file mode 100644 index 00000000..c3662f03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/src/infrastructure/domain.f90 @@ -0,0 +1,102 @@ +! src/infrastructure/domain.f90 +module domain_module + use base_modules, only: wp, ip, max_name_len + use config_module, only: cfd_config + use mesh_module, only: mesh_type + + implicit none + private + public :: wp, ip, domain_type, domain_create, is_physical_cell + + type :: domain_type + type(cfd_config), pointer :: config => null() + type(mesh_type), pointer :: mesh => null() + integer(ip) :: nghosts = 0 + integer(ip) :: ist = 1 ! 物理区域起始索引(1-based) + integer(ip) :: ied = 1 ! 物理区域结束索引(exclusive) + integer(ip) :: ntcells = 0 ! 总单元数(含ghost) + contains + procedure :: print_info => domain_print_info + procedure :: get_physical_indices => domain_get_physical_indices + end type domain_type + +contains + + function domain_create(config, mesh) result(domain) + type(cfd_config), target, intent(in) :: config + type(mesh_type), target, intent(in) :: mesh + type(domain_type) :: domain + + domain%config => config + domain%mesh => mesh + + ! 计算ghost层数(参考Julia的_calc_nghosts) + domain%nghosts = calc_nghosts(config) + domain%ist = domain%nghosts + 1 + domain%ied = domain%ist + mesh%ncells + domain%ntcells = mesh%ncells + 2 * domain%nghosts + + if (config%verbose) then + print *, "[DOMAIN] Created:" + print *, " Ghost layers: ", domain%nghosts + print *, " Physical cells: ", domain%ist, " to ", domain%ied - 1 + print *, " Total cells: ", domain%ntcells + end if + end function domain_create + + function calc_nghosts(config) result(nghosts) + type(cfd_config), intent(in) :: config + integer(ip) :: nghosts + + character(len=max_name_len) :: scheme + + scheme = config%recon_scheme + + if (scheme == "eno") then + nghosts = config%spatial_order + else if (index(scheme, "weno") > 0) then + nghosts = config%spatial_order / 2 + 1 + else + print *, "[WARNING] Unknown scheme, using default nghosts=2" + nghosts = 2 + end if + + if (nghosts <= 0) then + print *, "[ERROR] Invalid nghosts: ", nghosts + nghosts = 2 + end if + end function calc_nghosts + + logical function is_physical_cell(this, idx) + class(domain_type), intent(in) :: this + integer(ip), intent(in) :: idx + is_physical_cell = (idx >= this%ist .and. idx < this%ied) + end function is_physical_cell + + function domain_get_physical_indices(this) result(indices) + class(domain_type), intent(in) :: this + integer(ip), allocatable :: indices(:) + integer(ip) :: i, count + + count = this%ied - this%ist + allocate(indices(count)) + + do i = 1, count + indices(i) = this%ist + i - 1 + end do + end function domain_get_physical_indices + + subroutine domain_print_info(this) + class(domain_type), intent(in) :: this + + print *, "=== Domain Information ===" + print *, "Configuration: ", trim(this%config%recon_scheme), & + " order ", this%config%spatial_order + print *, "Ghost layers: ", this%nghosts + print *, "Physical cells: ", this%ist, " to ", this%ied - 1 + print *, "Total cells: ", this%ntcells + print *, "Mesh cells: ", this%mesh%ncells + print *, "==========================" + end subroutine domain_print_info + +end module domain_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/02f/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..f810f3a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/src/infrastructure/mesh.f90 @@ -0,0 +1,73 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use base_modules, only: wp, ip + + implicit none + public :: wp, ip, mesh_type, mesh_init, mesh_print_info + + ! 网格类型 + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer(ip) :: ncells = 40 + integer(ip) :: nnodes + integer(ip) :: nx + real(wp), allocatable :: x(:) ! 节点坐标 + real(wp), allocatable :: xcc(:) ! 单元中心坐标 + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer(ip), optional, intent(in) :: ncells + + integer(ip) :: i + + ! 设置参数 + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! 计算 + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! 分配内存 + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! 生成节点坐标 + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! 生成单元中心坐标 + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== Mesh Information ===" + print *, "Domain: [", this%xmin, ", ", this%xmax, "]" + print *, "Cells: ", this%ncells + print *, "Nodes: ", this%nnodes + print *, "dx: ", this%dx + print *, "L: ", this%L + print *, "========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/src/infrastructure/solution.f90 b/example/1d-linear-convection/weno3/fortran/registry/02f/src/infrastructure/solution.f90 new file mode 100644 index 00000000..ce88fd8a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/src/infrastructure/solution.f90 @@ -0,0 +1,131 @@ +! src/infrastructure/solution.f90 +module solution_module + use base_modules, only: wp, ip + use domain_module, only: domain_type + + implicit none + private + public :: wp, ip, solution_type, solution_create, solution_reset + + type :: solution_type + type(domain_type), pointer :: domain => null() + real(wp), allocatable :: u(:) ! 当前解(含ghost) + real(wp), allocatable :: un(:) ! 旧解 + real(wp), allocatable :: q_face_left(:) ! 左界面值 + real(wp), allocatable :: q_face_right(:)! 右界面值 + real(wp), allocatable :: flux(:) ! 通量 + real(wp), allocatable :: res(:) ! 残差 + contains + procedure :: initialize => solution_initialize + procedure :: update_old_field => solution_update_old_field + procedure :: print_info => solution_print_info + procedure :: reset => solution_reset_instance + end type solution_type + +contains + + function solution_create(domain) result(solution) + type(domain_type), target, intent(in) :: domain + type(solution_type) :: solution + + integer(ip) :: ncells, nnodes, ntcells + + solution%domain => domain + + ncells = domain%mesh%ncells + nnodes = domain%mesh%nnodes + ntcells = domain%ntcells + + ! 分配数组(与Julia solution.jl一致) + allocate(solution%u(ntcells), source=0.0_wp) + allocate(solution%un(ntcells), source=0.0_wp) + allocate(solution%q_face_left(nnodes), source=0.0_wp) + allocate(solution%q_face_right(nnodes), source=0.0_wp) + allocate(solution%flux(nnodes), source=0.0_wp) + allocate(solution%res(ncells), source=0.0_wp) + + if (domain%config%verbose) then + print *, "[SOLUTION] Created:" + print *, " u size: ", size(solution%u), " (with ghosts)" + print *, " flux size: ", size(solution%flux) + print *, " res size: ", size(solution%res) + end if + end function solution_create + + subroutine solution_initialize(this, initial_values) + class(solution_type), intent(inout) :: this + real(wp), intent(in), optional :: initial_values(:) + + integer(ip) :: i, idx + type(domain_type), pointer :: domain + + domain => this%domain + + if (present(initial_values)) then + ! 应用初始值到物理区域 + do i = domain%ist, domain%ied - 1 + idx = i - domain%ist + 1 + if (idx <= size(initial_values)) then + this%u(i) = initial_values(idx) + end if + end do + else + ! 默认为0 + this%u = 0.0_wp + end if + + ! 同步旧场(与Julia的update_old_field一致) + call this%update_old_field() + + if (domain%config%verbose) then + print *, "[SOLUTION] Initialized" + print *, " u range: ", minval(this%u), " to ", maxval(this%u) + end if + end subroutine solution_initialize + + subroutine solution_update_old_field(this) + class(solution_type), intent(inout) :: this + this%un = this%u ! 与Julia的 un .= u 一致 + end subroutine solution_update_old_field + + subroutine solution_reset_instance(this) + class(solution_type), intent(inout) :: this + call solution_reset(this) + end subroutine solution_reset_instance + + subroutine solution_reset(solution) + type(solution_type), intent(inout) :: solution + + if (allocated(solution%u)) solution%u = 0.0_wp + if (allocated(solution%un)) solution%un = 0.0_wp + if (allocated(solution%q_face_left)) solution%q_face_left = 0.0_wp + if (allocated(solution%q_face_right)) solution%q_face_right = 0.0_wp + if (allocated(solution%flux)) solution%flux = 0.0_wp + if (allocated(solution%res)) solution%res = 0.0_wp + + if (associated(solution%domain) .and. solution%domain%config%verbose) then + print *, "[SOLUTION] Reset" + end if + end subroutine solution_reset + + subroutine solution_print_info(this) + class(solution_type), intent(in) :: this + + print *, "=== Solution Information ===" + print *, "Arrays:" + print *, " u: ", size(this%u), " elements" + print *, " un: ", size(this%un), " elements" + print *, " q_face_left: ", size(this%q_face_left), " elements" + print *, " q_face_right: ", size(this%q_face_right), " elements" + print *, " flux: ", size(this%flux), " elements" + print *, " res: ", size(this%res), " elements" + + if (allocated(this%u)) then + print *, "Values:" + print *, " u min/max: ", minval(this%u), maxval(this%u) + print *, " un min/max: ", minval(this%un), maxval(this%un) + end if + print *, "============================" + end subroutine solution_print_info + +end module solution_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/src/manager/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02f/src/manager/CMakeLists.txt new file mode 100644 index 00000000..00c8bf49 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/src/manager/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/manager/CMakeLists.txt +message(STATUS "配置管理器模块...") + +# 创建管理器库 +add_library(manager STATIC + component_manager.f90 + component_factory.f90 +) + +# 明确依赖关系:管理器依赖所有其他模块 +target_link_libraries(manager + PRIVATE + core + infrastructure + reconstructor + flux +) + +# 设置模块输出目录 +set_target_properties(manager PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "管理器模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/src/manager/component_factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/02f/src/manager/component_factory.f90 new file mode 100644 index 00000000..114fedea --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/src/manager/component_factory.f90 @@ -0,0 +1,127 @@ +! src/manager/component_factory.f90 +module component_factory_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + use eno_reconstructor_module, only: eno_reconstructor, create_eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor, create_weno3_reconstructor + use rusanov_flux_module, only: rusanov_flux, create_rusanov_flux + + implicit none + private + public :: wp, create_reconstructor, create_flux_calculator + + ! 错误代码 + integer, parameter :: CM_SUCCESS = 0 + integer, parameter :: CM_ERROR_UNKNOWN_SCHEME = 1 + integer, parameter :: CM_ERROR_UNKNOWN_FLUX = 2 + integer, parameter :: CM_ERROR_INVALID_ORDER = 3 + +contains + + ! ==================== 重构器创建 ==================== + + function create_reconstructor(config, status) result(recon) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(reconstructor_base), allocatable :: recon + + character(len=20) :: scheme + integer :: order, error_code + + scheme = trim(adjustl(config%recon_scheme)) + order = config%spatial_order + + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating reconstructor: ", scheme, " order=", order + end if + + select case(scheme) + case('eno') + allocate(eno_reconstructor :: recon) + select type(recon) + type is(eno_reconstructor) + recon = create_eno_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case('weno3') + allocate(weno3_reconstructor :: recon) + select type(recon) + type is(weno3_reconstructor) + recon = create_weno3_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case default + error_code = CM_ERROR_UNKNOWN_SCHEME + if (config%verbose) then + print *, "[ERROR] Unknown reconstructor scheme: ", scheme + print *, " Available: eno, weno3" + end if + end select + + ! 检查阶数有效性 + if (error_code == CM_SUCCESS) then + if (order < 1) then + error_code = CM_ERROR_INVALID_ORDER + if (config%verbose) then + print *, "[ERROR] Invalid spatial order: ", order + end if + end if + end if + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Reconstructor creation failed" + end if + end function create_reconstructor + + ! ==================== 通量计算器创建 ==================== + + function create_flux_calculator(config, status) result(flux) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(flux_calculator_base), allocatable :: flux + + character(len=20) :: flux_type + integer :: error_code + + flux_type = trim(adjustl(config%flux_type)) + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating flux calculator: ", flux_type + end if + + select case(flux_type) + case('rusanov') + allocate(rusanov_flux :: flux) + select type(flux) + type is(rusanov_flux) + flux = create_rusanov_flux() + flux%wave_speed_default = config%wave_speed + end select + + case default + error_code = CM_ERROR_UNKNOWN_FLUX + if (config%verbose) then + print *, "[ERROR] Unknown flux type: ", flux_type + print *, " Available: rusanov" + end if + end select + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Flux calculator creation failed" + end if + end function create_flux_calculator + +end module component_factory_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/src/manager/component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/02f/src/manager/component_manager.f90 new file mode 100644 index 00000000..9e095c25 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/src/manager/component_manager.f90 @@ -0,0 +1,75 @@ +! src/manager/component_manager.f90 +module component_manager_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use component_factory_module, only: create_reconstructor, create_flux_calculator + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + implicit none + private + public :: wp, component_manager_info, validate_config + public :: create_reconstructor, create_flux_calculator + +contains + + ! ==================== 配置验证 ==================== + + function validate_config(config) result(is_valid) + type(cfd_config), intent(in) :: config + logical :: is_valid + + integer :: status + class(reconstructor_base), allocatable :: test_recon + class(flux_calculator_base), allocatable :: test_flux + + is_valid = .false. + + ! 测试创建重构器 + test_recon = create_reconstructor(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid reconstructor configuration" + end if + return + end if + + ! 测试创建通量计算器 + test_flux = create_flux_calculator(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid flux configuration" + end if + return + end if + + ! 清理测试组件 + if (allocated(test_recon)) deallocate(test_recon) + if (allocated(test_flux)) deallocate(test_flux) + + is_valid = .true. + + if (config%verbose) then + print *, "[CONFIG VALIDATION] Configuration is valid" + end if + end function validate_config + + ! ==================== 信息显示 ==================== + + subroutine component_manager_info() + print *, "=== Component Manager ===" + print *, "Available reconstructors:" + print *, " - eno (orders: 1-7)" + print *, " - weno3 (order: 3)" + print *, "" + print *, "Available flux calculators:" + print *, " - rusanov" + print *, "" + print *, "Features:" + print *, " - Configuration validation" + print *, " - Component creation from config" + print *, " - Error handling with status codes" + print *, "=========================" + end subroutine component_manager_info + +end module component_manager_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02f/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02f/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..3fde7746 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,28 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux STATIC + base.f90 + rusanov.f90 +) + +#target_link_libraries(flux PRIVATE core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 明确设置不使用 /Qm64 选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + set_target_properties(flux PROPERTIES + Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /Qm64" # 明确设置,避免继承问题 + ) +endif() + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/02f/src/numerics/flux/base.f90 new file mode 100644 index 00000000..7080a7ae --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/src/numerics/flux/base.f90 @@ -0,0 +1,30 @@ +!src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + procedure :: print_basic_info => flux_print_basic ! 添加辅助方法 + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + + subroutine flux_print_basic(this) + class(flux_calculator_base), intent(in) :: this + print *, " Name: ", trim(this%name) + end subroutine flux_print_basic + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/02f/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..daa9e3bb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/src/numerics/flux/rusanov.f90 @@ -0,0 +1,41 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux, create_rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + + ! 构造函数接口 + interface rusanov_flux + module procedure create_rusanov_flux + end interface + +contains + + ! 构造函数 + type(rusanov_flux) function create_rusanov_flux() result(this) + this%name = "Rusanov" + this%wave_speed_default = 1.0_real64 + end function create_rusanov_flux + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Flux calculator information:" + call this%print_basic_info() + + ! 添加Rusanov特有信息 + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02f/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..5e4b938d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,22 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor STATIC + base.f90 + eno.f90 + weno3.f90 +) + +target_link_libraries(reconstructor PRIVATE core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/02f/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..53798d02 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/src/numerics/reconstructor/base.f90 @@ -0,0 +1,33 @@ +!src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + procedure :: print_basic_info => reconstructor_print_basic ! 添加一个辅助方法 + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + + subroutine reconstructor_print_basic(this) + class(reconstructor_base), intent(in) :: this + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_print_basic + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/02f/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..f973e8b3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor, create_eno_reconstructor ! ← 添加这个 + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + + ! 构造函数接口 + interface eno_reconstructor + module procedure create_eno_reconstructor + end interface + +contains + + ! 构造函数 + type(eno_reconstructor) function create_eno_reconstructor() result(this) + this%name = "ENO" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_eno_reconstructor + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加ENO特有信息 + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/02f/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..d5b7a747 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno3_reconstructor, create_weno3_reconstructor + + type, extends(reconstructor_base) :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + + ! 构造函数接口 + interface weno3_reconstructor + module procedure create_weno3_reconstructor + end interface + +contains + + ! 构造函数 + type(weno3_reconstructor) function create_weno3_reconstructor() result(this) + this%name = "WENO3" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_weno3_reconstructor + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO3特有信息 + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02f/tests/CMakeLists.txt new file mode 100644 index 00000000..d23f7fe4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/tests/CMakeLists.txt @@ -0,0 +1,78 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +# +#message(STATUS "CMAKE_Fortran_MODULE_DIRECTORY=${CMAKE_Fortran_MODULE_DIRECTORY}") +# +add_executable(test_minimal_simple test_minimal_simple.f90) +target_link_libraries(test_minimal_simple + PRIVATE + core # 必须链接core库 + infrastructure +) + + +add_executable(test_simple_link test_simple_link.f90) +target_link_libraries(test_simple_link + PRIVATE + reconstructor + flux +) + + +add_executable(test_factory_simple test_factory_simple.f90) +target_link_libraries(test_factory_simple + PRIVATE + core + infrastructure + reconstructor + flux +) + + +add_executable(test_component_manager test_component_manager.f90) + +target_link_libraries(test_component_manager + PRIVATE + manager # ← 链接到新的管理器库 + infrastructure +) +# +#add_executable(test_architecture test_architecture.f90) +#target_link_libraries(test_architecture +# PRIVATE +# core +# infrastructure +# manager +#) + +#add_executable(test_solver_framework test_solver_framework.f90) +#target_link_libraries(test_solver_framework +# PRIVATE +# core +# infrastructure +# manager +#) +# +#add_executable(test_cfd_architecture test_cfd_architecture.f90) +#target_link_libraries(test_cfd_architecture +# PRIVATE +# base +# core +# infrastructure +#) + + +add_executable(test_basic_only test_basic_only.f90) +target_link_libraries(test_basic_only + PRIVATE + infrastructure + core +) + +add_executable(test_domain_solution test_domain_solution.f90) +target_link_libraries(test_domain_solution + PRIVATE + infrastructure + core +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_basic_only.f90 b/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_basic_only.f90 new file mode 100644 index 00000000..20901ddc --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_basic_only.f90 @@ -0,0 +1,56 @@ +! tests/test_basic_only.f90 +program test_basic_only + ! 只测试最基本的功能,不依赖复杂模块 + use config_module, only: cfd_config, config_print, wp + use mesh_module, only: mesh_type + use registry_module, only: registry_init, registry_cleanup, & + register_component_simple, list_components + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "=== BASIC TEST - Minimal Functionality ===" + print *, "" + + ! 测试1: 配置 + print *, "1. Testing configuration..." + print *, "----------------------------" + call config_print(config) + print *, "" + + ! 测试2: 网格 + print *, "2. Testing mesh..." + print *, "------------------" + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=5) + print *, "Mesh initialized:" + print *, " Cells: ", mesh%ncells + print *, " Nodes: ", mesh%nnodes + print *, " dx: ", mesh%dx + print *, "" + + ! 测试3: 注册系统 + print *, "3. Testing registry..." + print *, "----------------------" + + call registry_init() + + ! 注册组件(使用简化版本) + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! 列出组件 + call list_components() + print *, "" + + ! 清理 + call registry_cleanup() + + print *, "=== TEST PASSED ===" + print *, "✓ Configuration works" + print *, "✓ Mesh works" + print *, "✓ Registry works" + +end program test_basic_only \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_cfd_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_cfd_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_cfd_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_component_manager.f90 new file mode 100644 index 00000000..f60c3505 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_component_manager.f90 @@ -0,0 +1,111 @@ +! tests/test_component_manager.f90 +program test_component_manager + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use component_manager_module, only: create_reconstructor, create_flux_calculator + use component_manager_module, only: component_manager_info, validate_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + implicit none + + type(cfd_config) :: config + class(reconstructor_base), allocatable :: recon + class(flux_calculator_base), allocatable :: flux + integer :: status + logical :: is_valid + + print *, "=== Component Manager Test ===" + print *, "" + + ! 显示组件管理器信息 + call component_manager_info() + print *, "" + + ! 测试1: 基本配置 + print *, "1. Testing basic ENO3 + Rusanov configuration..." + print *, "-----------------------------------------------" + + config%verbose = .true. + call config_print(config) + + ! 配置ENO3重构 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + call config_print(config) + print *, "" + + ! 验证配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Configuration is valid" + else + print *, "[ERROR] Configuration is invalid" + end if + print *, "" + + ! 测试2: 创建组件 + print *, "2. Testing component creation..." + print *, "--------------------------------" + + ! 创建重构器(带状态检查) + recon = create_reconstructor(config, status) + if (status == 0) then + print *, "[OK] Reconstructor created successfully" + call recon%info() + else + print *, "[ERROR] Failed to create reconstructor, code:", status + end if + print *, "" + + ! 创建通量计算器 + flux = create_flux_calculator(config, status) + if (status == 0) then + print *, "[OK] Flux calculator created successfully" + call flux%info() + else + print *, "[ERROR] Failed to create flux calculator, code:", status + end if + print *, "" + + ! 测试3: WENO3重构测试 + print *, "3. Testing WENO3 configuration..." + print *, "---------------------------------" + + call config_with_reconstruction(config, "weno3", 3) + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] WENO3 configuration is valid" + + ! 创建WENO3重构器 + recon = create_reconstructor(config) + call recon%info() + else + print *, "[ERROR] WENO3 configuration is invalid" + end if + print *, "" + + ! 测试4: 错误配置测试 + print *, "4. Testing invalid configuration..." + print *, "-----------------------------------" + + config%recon_scheme = "unknown_scheme" + config%flux_type = "unknown_flux" + + is_valid = validate_config(config) + if (.not. is_valid) then + print *, "[OK] Invalid configuration correctly rejected" + else + print *, "[ERROR] Invalid configuration should have been rejected" + end if + + ! 清理 + if (allocated(recon)) deallocate(recon) + if (allocated(flux)) deallocate(flux) + + print *, "" + print *, "=== Component manager test completed successfully ===" + +end program test_component_manager \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_domain_solution.f90 b/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_domain_solution.f90 new file mode 100644 index 00000000..ff659bac --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_domain_solution.f90 @@ -0,0 +1,102 @@ +! tests/test_domain_solution.f90 +program test_domain_solution + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create, solution_reset + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + real(wp), allocatable :: initial_values(:) + integer :: i + + print *, "=== Domain and Solution Test ===" + print *, "" + + ! 测试1: 不同重构方案的ghost层计算 + print *, "1. Testing ghost layer calculation..." + print *, "--------------------------------------" + + ! ENO3 + call config_with_reconstruction(config, "eno", 3) + config%verbose = .false. + call mesh%init(ncells=10) + domain = domain_create(config, mesh) + print *, "ENO3: nghosts = ", domain%nghosts, " (expected: 3)" + + ! WENO3 + call config_with_reconstruction(config, "weno3", 3) + domain = domain_create(config, mesh) + print *, "WENO3: nghosts = ", domain%nghosts, " (expected: 2)" + + ! WENO5 + call config_with_reconstruction(config, "weno", 5) + domain = domain_create(config, mesh) + print *, "WENO5: nghosts = ", domain%nghosts, " (expected: 3)" + print *, "" + + ! 测试2: Solution数组 + print *, "2. Testing solution arrays..." + print *, "------------------------------" + + call config_with_reconstruction(config, "eno", 3) + config%verbose = .true. + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + domain = domain_create(config, mesh) + call domain%print_info() + print *, "" + + solution = solution_create(domain) + call solution%print_info() + print *, "" + + ! 测试3: 初始化和更新 + print *, "3. Testing initialization and update..." + print *, "----------------------------------------" + + allocate(initial_values(mesh%ncells)) + do i = 1, mesh%ncells + initial_values(i) = sin(2.0_wp * 3.14159265358979_wp * mesh%xcc(i) / mesh%L) + end do + + call solution%initialize(initial_values) + print *, "After initialization:" + print *, " u range: ", minval(solution%u), " to ", maxval(solution%u) + print *, " un range: ", minval(solution%un), " to ", maxval(solution%un) + + ! 修改当前解,测试更新 + solution%u = solution%u * 2.0_wp + call solution%update_old_field() + print *, "After update: max|u - un| = ", maxval(abs(solution%u - solution%un)) + print *, "" + + ! 测试4: 重置 + print *, "4. Testing reset..." + print *, "-------------------" + + call solution_reset(solution) + print *, "After reset:" + print *, " u max: ", maxval(abs(solution%u)) + print *, " un max: ", maxval(abs(solution%un)) + print *, " flux max: ", maxval(abs(solution%flux)) + print *, "" + + deallocate(initial_values) + + print *, "=== Test Summary ===" + print *, "✓ Ghost layer calculation works" + print *, "✓ Domain creation works" + print *, "✓ Solution arrays work" + print *, "✓ Initialization works" + print *, "✓ Field update works" + print *, "✓ Reset works" + print *, "" + print *, "Ready for next step: Implementing Physics modules" + +end program test_domain_solution \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_factory_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_factory_simple.f90 new file mode 100644 index 00000000..db65da7c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_factory_simple.f90 @@ -0,0 +1,58 @@ +! tests/test_factory_simple.f90 (修复版) +program test_factory_simple + use base_modules, only: wp ! ← 添加这行 + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Basic systems + print *, "1. Testing basic systems..." + print *, "-----------------------------" + call config_print(config) + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Creating reconstructors + print *, "2. Testing reconstructors..." + print *, "------------------------------" + + ! 创建并测试ENO重构器 + print *, "Creating ENO reconstructor..." + eno = eno_reconstructor() ! 使用构造函数 + call eno%info() ! 必须调用info方法 + + print *, "" + print *, "Creating WENO3 reconstructor..." + weno3 = weno3_reconstructor() ! 使用构造函数 + call weno3%info() ! 必须调用info方法 + print *, "" + + ! Test 3: Creating flux calculator + print *, "3. Testing flux calculator..." + print *, "-------------------------------" + + print *, "Creating Rusanov flux calculator..." + rusanov = rusanov_flux() ! 使用构造函数 + call rusanov%info() ! 必须调用info方法 + print *, "" + + print *, "=== Factory pattern simple test completed successfully ===" + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_minimal_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_minimal_simple.f90 new file mode 100644 index 00000000..ec03ccf8 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_minimal_simple.f90 @@ -0,0 +1,87 @@ +! tests/test_minimal_simple.f90 +program test_minimal_simple + use base_modules, only: wp ! ← 添加这行 + use registry_module + use config_module + use mesh_module + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Simple Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call registry_init() + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call list_components() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + + if (has_component_simple("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component_simple("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components (simple version) + print *, "5. Testing registry functionality" + print *, "---------------------------------" + print *, "Registry initialized: ", registry_is_initialized() + print *, "Number of components: ", registry_get_size() + print *, "" + + ! Cleanup + call registry_cleanup() + + print *, "=== Simple test completed successfully ===" + +end program test_minimal_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_physics_equations.f90 b/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_physics_equations.f90 new file mode 100644 index 00000000..379cb862 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_physics_equations.f90 @@ -0,0 +1,82 @@ +! tests/test_physics_equations.f90 +program test_physics_equations + use precision, only: dp + use linear_convection_equation, only: linear_convection_flux, linear_convection_speed + use linear_convection_problem, only: linear_convection_prob, create_linear_convection_problem + implicit none + + real(dp) :: u, f, a + type(linear_convection_prob) :: prob + real(dp), allocatable :: x(:), u_ic(:) + integer :: i, nx = 10 + + write(*,*) "=== 测试物理方程模块 ===" + + ! 测试线性对流方程 + write(*,*) "1. 测试线性对流方程..." + u = 2.5_dp + f = linear_convection_flux(u) + a = linear_convection_speed() + + write(*,*) " u = ", u + write(*,*) " F(u) = a*u = ", f + write(*,*) " a = ", a + + if (abs(f - 2.5_dp) < 1e-10_dp) then + write(*,*) " ✓ 通量计算正确" + else + write(*,*) " ✗ 通量计算错误" + end if + + ! 测试线性对流问题 + write(*,*) "2. 测试线性对流问题..." + prob = create_linear_convection_problem(wave_speed=2.0_dp, & + ic_type="step", & + boundary_type="periodic") + + write(*,*) " 波速: ", prob%wave_speed + write(*,*) " 初始条件类型: ", trim(prob%ic_type) + write(*,*) " 边界类型: ", trim(prob%boundary_type) + + ! 测试通量方法 + u = 1.5_dp + f = prob%flux(u) + a = prob%speed() + + write(*,*) " 问题通量 F(u) = ", f, " (期望: 3.0)" + write(*,*) " 问题波速 a = ", a, " (期望: 2.0)" + + if (abs(f - 3.0_dp) < 1e-10_dp) then + write(*,*) " ✓ 问题通量计算正确" + else + write(*,*) " ✗ 问题通量计算错误" + end if + + ! 测试初始条件 + write(*,*) "3. 测试初始条件..." + allocate(x(nx), u_ic(nx)) + do i = 1, nx + x(i) = 0.0_dp + (i-1) * 0.2_dp + end do + + call prob%initial_condition(x, u_ic, nx) + + write(*,*) " x 范围: ", x(1), " 到 ", x(nx) + write(*,*) " u_ic 范围: ", minval(u_ic), " 到 ", maxval(u_ic) + + ! 检查阶跃函数 + if (prob%ic_type == "step") then + if (abs(u_ic(1) - 1.0_dp) < 1e-10_dp .and. & + abs(u_ic(6) - 2.0_dp) < 1e-10_dp) then + write(*,*) " ✓ 阶跃初始条件正确" + else + write(*,*) " ✗ 阶跃初始条件错误" + end if + end if + + deallocate(x, u_ic) + + write(*,*) "=== 物理方程模块测试完成 ===" + write(*,*) "下一步: 实现初始条件工厂" + +end program test_physics_equations \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_simple_link.f90 b/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_simple_link.f90 new file mode 100644 index 00000000..71cc614e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02f/tests/test_simple_link.f90 @@ -0,0 +1,78 @@ +! tests/test_simple_link.f90 +program test_simple_link + use base_modules, only: wp ! ← 添加这行 + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call registry_init() + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call list_components() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component_simple("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component_simple("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Cleanup + call registry_cleanup() + + print *, "=== Minimal test completed successfully ===" + +end program test_simple_link \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02g/CMakeLists.txt new file mode 100644 index 00000000..ef66d584 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/README.md b/example/1d-linear-convection/weno3/fortran/registry/02g/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/02g/scripts/build.bat new file mode 100644 index 00000000..6fd6dc03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/scripts/build.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Project Builder +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python build script with full Intel environment support... +echo. + +python build.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Build failed + pause + exit /b 1 +) + +echo. +echo [INFO] Build completed successfully! +echo. +echo [INFO] To run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/02g/scripts/build.py new file mode 100644 index 00000000..3bf6d537 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/scripts/build.py @@ -0,0 +1,629 @@ +#!/usr/bin/env python3 +""" +Fortran CFD Project Builder - 完整Python解决方案 +在Python内部处理Intel oneAPI环境配置 +""" + +import os +import sys +import subprocess +import shutil +import argparse +import time +import platform +import tempfile +from pathlib import Path + +class IntelEnvironment: + """Intel oneAPI环境管理器""" + + def __init__(self): + self.setvars_path = None + self.env_vars = {} + + def find_setvars(self): + """查找setvars.bat文件""" + possible_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + r"C:\Program Files (x86)\Intel\oneAPI\compiler\latest\env\vars.bat", + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\setvars.bat"), + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\compiler\latest\env\vars.bat"), + ] + + for path in possible_paths: + if os.path.exists(path): + self.setvars_path = path + return True + + return False + + def setup_environment(self): + """设置Intel环境""" + if not self.find_setvars(): + return False + + try: + # 创建临时的批处理文件来捕获环境变量 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + f.write(f'@echo off\n') + f.write(f'call "{self.setvars_path}" >nul 2>&1\n') + f.write(f'set\n') # 输出所有环境变量 + temp_bat = f.name + + # 运行批处理文件并捕获输出 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + shell=True + ) + + # 解析环境变量 + for line in result.stdout.split('\n'): + line = line.strip() + if '=' in line: + key, value = line.split('=', 1) + self.env_vars[key.strip()] = value.strip() + + # 清理临时文件 + os.unlink(temp_bat) + + # 更新当前进程的环境变量 + os.environ.update(self.env_vars) + + return True + + except Exception as e: + print(f"设置Intel环境失败: {e}") + return False + + def get_compiler_info(self): + """获取编译器信息""" + info = {} + + # 检查ifx编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + env={**os.environ, **self.env_vars} if self.env_vars else os.environ + ) + + if result.returncode == 0: + for line in result.stdout.split('\n'): + if 'Version' in line or '版本' in line: + info['ifx_version'] = line.strip() + break + except: + pass + + # 检查环境变量 + info['ifx_root'] = self.env_vars.get('IFX_ROOT', '') + info['compiler_root'] = self.env_vars.get('ONEAPI_ROOT', '') + + return info + +class BuildSystem: + """构建系统主类""" + + def __init__(self): + self.project_root = Path(__file__).parent.parent + self.build_dir = self.project_root / "build" + self.intel_env = IntelEnvironment() + + # 设置控制台编码 + if sys.platform == "win32": + try: + import ctypes + # 设置控制台输出为UTF-8 + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + except: + pass + + def print_header(self, text): + """打印标题""" + print(f"\n{'='*70}") + print(f" {text}") + print(f"{'='*70}\n") + + def print_step(self, step, total, message): + """打印步骤""" + print(f"[{step}/{total}] {message}...") + + def print_success(self, message): + """打印成功""" + print(f"\033[92m✓ {message}\033[0m") + + def print_error(self, message): + """打印错误""" + print(f"\033[91m✗ {message}\033[0m") + + def print_warning(self, message): + """打印警告""" + print(f"\033[93m! {message}\033[0m") + + def print_info(self, message): + """打印信息""" + print(f"\033[94mℹ {message}\033[0m") + + def check_prerequisites(self): + """检查前提条件""" + self.print_step(1, 6, "检查前提条件") + + # 检查Python版本 + python_version = sys.version.split()[0] + self.print_info(f"Python版本: {python_version}") + + # 检查平台 + self.print_info(f"平台: {platform.system()} {platform.release()}") + self.print_info(f"处理器核心数: {os.cpu_count()}") + + # 检查CMake + try: + result = subprocess.run( + ["cmake", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + version_line = result.stdout.split('\n')[0] + self.print_success(f"CMake: {version_line}") + else: + self.print_error("CMake未找到") + return False + except FileNotFoundError: + self.print_error("CMake未安装") + return False + + return True + + def setup_intel_environment(self, args): + """设置Intel环境""" + self.print_step(2, 6, "配置Intel oneAPI环境") + + if not self.intel_env.find_setvars(): + self.print_warning("未找到Intel oneAPI setvars.bat") + self.print_info("将尝试使用系统环境中的编译器") + + # 检查是否能直接访问编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + self.print_success("Intel编译器在系统PATH中找到") + return True + else: + self.print_warning("Intel编译器未在PATH中找到") + except: + self.print_warning("无法访问Intel编译器") + + return True # 继续,让CMake自己找编译器 + + # 设置环境 + if self.intel_env.setup_environment(): + compiler_info = self.intel_env.get_compiler_info() + + if compiler_info.get('ifx_version'): + self.print_success(f"Intel Fortran编译器: {compiler_info['ifx_version']}") + elif compiler_info.get('ifx_root'): + self.print_success(f"Intel编译器路径: {compiler_info['ifx_root']}") + else: + self.print_success("Intel oneAPI环境配置完成") + + return True + else: + self.print_warning("Intel环境配置失败,将继续使用系统环境") + return True + + def clean_build_directory(self, args): + """清理构建目录""" + if args.clean and self.build_dir.exists(): + self.print_info("清理构建目录...") + try: + shutil.rmtree(self.build_dir) + self.print_success("构建目录已清理") + except Exception as e: + self.print_error(f"清理失败: {e}") + if not args.force: + return False + return True + + def run_command(self, cmd, cwd=None, check=True, env=None): + """运行命令""" + if isinstance(cmd, list): + cmd_str = ' '.join(str(c) for c in cmd if c) + else: + cmd_str = str(cmd) + + print(f" \033[96m$\033[0m {cmd_str}") + + try: + # 合并环境变量 + exec_env = os.environ.copy() + if env: + exec_env.update(env) + if self.intel_env.env_vars: + exec_env.update(self.intel_env.env_vars) + + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=False, + env=exec_env + ) + + # 处理输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or '完成' in line or '生成' in line: + print(f" \033[92m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + if check and result.returncode != 0: + self.print_error(f"命令执行失败,退出码: {result.returncode}") + return False + + return True + + except Exception as e: + self.print_error(f"命令执行异常: {e}") + return False + + def configure_cmake(self, args): + """配置CMake""" + self.print_step(3, 6, "配置CMake项目") + + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ] + + if args.compiler == "ifx": + cmake_cmd.extend(["-T", "fortran=ifx"]) + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + success = self.run_command(cmake_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("CMake配置完成") + else: + self.print_error("CMake配置失败") + + return success + + def build_project(self, args): + """构建项目""" + self.print_step(4, 6, "构建项目") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + success = self.run_command(build_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("项目构建完成") + else: + self.print_error("构建失败") + + return success + + def run_tests_with_environment(self, test_exe): + """运行单个测试,确保有Intel环境""" + try: + # 创建临时的批处理文件来运行测试 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'"{test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'"{test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + return result + + except Exception as e: + print(f"运行测试失败: {e}") + return None + + def run_tests(self, args): + """运行测试""" + self.print_step(5, 6, "运行测试") + + # 查找测试可执行文件 + test_dir = self.build_dir / "bin" / args.build_type + if not test_dir.exists(): + test_dir = self.build_dir / "bin" + if not test_dir.exists(): + test_dir = self.build_dir + + test_files = list(test_dir.glob("test_*.exe")) + + if not test_files: + self.print_warning("未找到测试程序") + return True + + all_passed = True + + for test_exe in sorted(test_files): + test_name = test_exe.stem + self.print_info(f"运行测试: {test_name}") + print(f" {'-'*50}") + + # 运行测试 + result = self.run_tests_with_environment(str(test_exe)) + + if result is None: + self.print_error(f" {test_name} 运行失败") + all_passed = False + continue + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + print(f" {line}") + + if result.returncode == 0: + self.print_success(f" {test_name} 通过") + else: + self.print_error(f" {test_name} 失败 (退出码: {result.returncode})") + all_passed = False + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + print() # 空行 + + return all_passed + + def create_test_runner(self, args): + """创建独立的测试运行器""" + self.print_step(6, 6, "创建测试运行器") + + runner_path = self.build_dir / "run_tests.bat" + + content = f'''@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Test Runner +echo ======================================== +echo. + +REM Setup Intel oneAPI environment +set "SETVARS_PATH={self.intel_env.setvars_path or ''}" +if exist "%SETVARS_PATH%" ( + call "%SETVARS_PATH%" >nul + echo [INFO] Intel environment configured +) else ( + echo [WARNING] Intel environment not found + echo [WARNING] Tests may fail without runtime libraries +) + +echo. + +REM Run all test executables +set "TEST_COUNT=0" +set "PASS_COUNT=0" + +for %%f in ("bin\\{args.build_type}\\test_*.exe") do ( + set /a TEST_COUNT+=1 + echo [TEST %%f] + echo {'-'*50} + + %%f + if errorlevel 1 ( + echo [FAILED] %%f + ) else ( + echo [PASSED] %%f + set /a PASS_COUNT+=1 + ) + echo. +) + +echo ======================================== +echo Tests: %PASS_COUNT%/%TEST_COUNT% passed +if %PASS_COUNT% equ %TEST_COUNT% ( + echo [SUCCESS] All tests passed! +) else ( + echo [FAILURE] Some tests failed +) +echo ======================================== + +pause +''' + + with open(runner_path, 'w', encoding='utf-8') as f: + f.write(content) + + self.print_success(f"测试运行器已创建: {runner_path}") + self.print_info(f"使用方法: cd build && run_tests.bat") + + return runner_path + + def generate_report(self, args, build_time, tests_passed): + """生成构建报告""" + self.print_header("构建完成") + + print(f"项目: {self.project_root.name}") + print(f"构建类型: {args.build_type}") + print(f"编译器: {args.compiler}") + print(f"并行作业: {args.jobs}") + print(f"总耗时: {build_time:.1f}秒") + print(f"测试结果: {'全部通过' if tests_passed else '有失败'}") + + # 显示生成的可执行文件 + bin_dir = self.build_dir / "bin" / args.build_type + if bin_dir.exists(): + print(f"\n生成的可执行文件:") + for exe in sorted(bin_dir.glob("*.exe")): + size_mb = exe.stat().st_size / (1024 * 1024) + print(f" • {exe.name} ({size_mb:.2f} MB)") + + # 显示测试运行器信息 + runner_path = self.build_dir / "run_tests.bat" + if runner_path.exists(): + print(f"\n独立测试运行器:") + print(f" • {runner_path.name}") + print(f" 在Intel oneAPI环境中运行所有测试") + + def run(self): + """运行构建系统""" + parser = argparse.ArgumentParser( + description="Fortran CFD项目构建工具", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认构建 + %(prog)s --clean # 清理后构建 + %(prog)s --build-type Release # Release构建 + %(prog)s --no-tests # 只构建,不运行测试 + %(prog)s -j8 --verbose # 8线程并行构建,详细输出 + """ + ) + + parser.add_argument("--build-type", choices=["Debug", "Release"], + default="Debug", help="构建类型") + parser.add_argument("--compiler", choices=["ifx", "ifort"], + default="ifx", help="Fortran编译器") + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-tests", action="store_true", + help="跳过测试") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + + args = parser.parse_args() + + # 开始构建 + start_time = time.time() + + self.print_header("Fortran CFD 项目构建系统") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 检查前提条件 + if not self.check_prerequisites(): + if not args.force: + return 1 + + # 2. 设置Intel环境 + if not self.setup_intel_environment(args): + if not args.force: + return 1 + + # 3. 清理目录 + if not self.clean_build_directory(args): + if not args.force: + return 1 + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 4. 配置CMake + if not self.configure_cmake(args): + if not args.force: + return 1 + + # 5. 构建项目 + if not self.build_project(args): + if not args.force: + return 1 + + # 6. 运行测试和创建测试运行器 + tests_passed = True + if not args.no_tests: + tests_passed = self.run_tests(args) + + # 创建测试运行器 + self.create_test_runner(args) # 传递 args 参数 + + # 7. 生成报告 + build_time = time.time() - start_time + self.generate_report(args, build_time, tests_passed) + + return 0 if tests_passed else 1 + + except KeyboardInterrupt: + self.print_error("\n构建被用户中断") + return 1 + except Exception as e: + self.print_error(f"构建过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + builder = BuildSystem() + return builder.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/02g/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/scripts/run_step1.bat b/example/1d-linear-convection/weno3/fortran/registry/02g/scripts/run_step1.bat new file mode 100644 index 00000000..0b6b1f17 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/scripts/run_step1.bat @@ -0,0 +1,39 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Step 1: Physics Modules Test +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python step1 script with full Intel environment support... +echo. + +python run_step1.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Step 1 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Step 1 completed successfully! +echo. +echo [INFO] Next step: Update config to include physics settings +echo [INFO] Run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/scripts/run_step1.py b/example/1d-linear-convection/weno3/fortran/registry/02g/scripts/run_step1.py new file mode 100644 index 00000000..5e087a69 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/scripts/run_step1.py @@ -0,0 +1,319 @@ +#!/usr/bin/env python3 +""" +Step 1: Physics Modules Test +扩展build.py,专门用于测试物理模块 +""" + +import os +import sys +import subprocess +import time +from pathlib import Path + +# 添加当前目录到路径,以便导入build.py的类 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import IntelEnvironment, BuildSystem +except ImportError: + print("Error: Cannot import build.py. Make sure build.py is in the same directory.") + sys.exit(1) + +class Step1System(BuildSystem): + """Step 1 测试系统,继承自BuildSystem""" + + def __init__(self): + super().__init__() + self.test_name = "test_physics_minimal" + self.test_exe = None + + def find_test_executable(self): + """查找测试可执行文件""" + possible_paths = [ + self.build_dir / "bin" / "Debug" / f"{self.test_name}.exe", + self.build_dir / "Debug" / f"{self.test_name}.exe", + self.build_dir / f"{self.test_name}.exe", + self.build_dir / "bin" / f"{self.test_name}.exe", + ] + + for path in possible_paths: + if path.exists(): + self.test_exe = path + self.print_success(f"Found test executable: {path}") + return True + + # 如果没有找到,尝试搜索 + self.print_warning(f"Could not find {self.test_name}.exe") + self.print_info("Searching for test executables...") + + try: + result = subprocess.run( + ["dir", str(self.build_dir), "/s", "/b", "*.exe"], + capture_output=True, + text=True, + encoding='utf-8', + shell=True + ) + + if result.returncode == 0: + test_files = [line.strip() for line in result.stdout.split('\n') + if line and 'test_' in line.lower()] + + if test_files: + self.print_info("Found test files:") + for test_file in test_files: + self.print_info(f" {test_file}") + return False + except: + pass + + return False + + def run_test_with_intel_env(self): + """在Intel环境下运行测试""" + if not self.test_exe: + self.print_error("No test executable found") + return False + + self.print_step(1, 2, f"Running test: {self.test_exe.name}") + + try: + # 创建临时的批处理文件来运行测试(包含Intel环境) + import tempfile + + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + # 设置Intel环境 + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'echo [INFO] Intel environment configured\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'echo [WARNING] Intel environment not found\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + self.print_info(f"Command: {temp_bat}") + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower() or 'fail' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or 'pass' in line.lower() or '✓' in line: + print(f" \033[92m{line}\033[0m") + elif '=' in line or '---' in line: + print(f" \033[96m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + self.print_warning("Test stderr output:") + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + self.print_step(2, 2, "Test execution completed") + + if result.returncode == 0: + self.print_success("Test passed") + return True + else: + self.print_error(f"Test failed (exit code: {result.returncode})") + return False + + except Exception as e: + self.print_error(f"Failed to run test: {e}") + return False + + def build_project_if_needed(self, args): + """如果需要,构建项目""" + if args.no_build: + self.print_info("Skipping build (--no-build flag)") + return True + + self.print_step(1, 3, "Building project") + + # 调用父类的构建方法 + build_args = argparse.Namespace() + build_args.clean = args.clean + build_args.build_type = "Debug" + build_args.compiler = "ifx" + build_args.no_tests = True # 不运行所有测试 + build_args.jobs = os.cpu_count() + build_args.verbose = args.verbose + build_args.force = args.force + + # 清理构建目录 + if args.clean and self.build_dir.exists(): + self.print_info("Cleaning build directory...") + import shutil + try: + shutil.rmtree(self.build_dir) + self.print_success("Build directory cleaned") + except Exception as e: + self.print_error(f"Clean failed: {e}") + if not args.force: + return False + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 配置CMake + self.print_step(2, 3, "Configuring CMake") + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-DCMAKE_BUILD_TYPE=Debug", + "-T", "fortran=ifx", + ] + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + if not self.run_command(cmake_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + # 构建项目 + self.print_step(3, 3, "Building project") + build_cmd = [ + "cmake", + "--build", ".", + "--config", "Debug", + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + if not self.run_command(build_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + self.print_success("Build completed") + return True + + def run(self): + """运行Step 1测试""" + parser = argparse.ArgumentParser( + description="Step 1: Physics Modules Test", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认运行 + %(prog)s --clean # 清理后构建并测试 + %(prog)s --no-build # 只运行测试,不重新构建 + %(prog)s --verbose # 详细输出 + %(prog)s -j4 # 使用4个并行作业构建 + """ + ) + + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-build", action="store_true", + help="不重新构建,直接运行测试") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + + args = parser.parse_args() + + # 开始测试 + start_time = time.time() + + self.print_header("Step 1: Physics Modules Implementation Test") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 设置Intel环境 + self.print_step(1, 4, "Setting up Intel oneAPI environment") + if not self.setup_intel_environment(args): + if not args.force: + self.print_error("Intel environment setup failed") + return 1 + self.print_warning("Intel environment setup failed, continuing...") + + # 2. 构建项目(如果需要) + self.print_step(2, 4, "Building project if needed") + if not self.build_project_if_needed(args): + if not args.force: + return 1 + + # 3. 查找测试可执行文件 + self.print_step(3, 4, "Finding test executable") + if not self.find_test_executable(): + if not args.force: + return 1 + self.print_warning("Test executable not found, but continuing due to --force") + return 0 + + # 4. 运行测试 + self.print_step(4, 4, "Running physics module test") + test_passed = self.run_test_with_intel_env() + + # 生成报告 + test_time = time.time() - start_time + self.print_header("Step 1 Complete") + + print(f"测试: {'通过 ✓' if test_passed else '失败 ✗'}") + print(f"测试程序: {self.test_exe.name if self.test_exe else '未找到'}") + print(f"总耗时: {test_time:.1f}秒") + + if test_passed: + print(f"\n下一步: 更新配置以包含物理设置") + print(f"建议: 修改config.f90,添加physics相关字段") + return 0 + else: + if args.force: + self.print_warning("测试失败,但由于--force标志继续执行") + return 0 + return 1 + + except KeyboardInterrupt: + self.print_error("\n测试被用户中断") + return 1 + except Exception as e: + self.print_error(f"测试过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + system = Step1System() + return system.run() + +if __name__ == "__main__": + # 需要导入argparse + import argparse + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02g/src/CMakeLists.txt new file mode 100644 index 00000000..8a76bdb5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/CMakeLists.txt @@ -0,0 +1,17 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(base) +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) +add_subdirectory(physics) # ← 新增物理模块目录 +add_subdirectory(manager) + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/base/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02g/src/base/CMakeLists.txt new file mode 100644 index 00000000..74f4aa65 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/base/CMakeLists.txt @@ -0,0 +1,16 @@ +# src/base/CMakeLists.txt +message(STATUS "Configuring base module...") + +add_library(base STATIC + modules.f90 + precision.f90 # 新增 +) + +set_target_properties(base PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Base module configured") + +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/base/modules.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/src/base/modules.f90 new file mode 100644 index 00000000..43aaee24 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/base/modules.f90 @@ -0,0 +1,36 @@ +! src/base/modules.f90 +module base_modules + use, intrinsic :: iso_fortran_env, only: real64, int32 + implicit none + + public :: wp, ip, max_name_len, string_len, cfd_config_base, component_info + + integer, parameter :: wp = real64 + integer, parameter :: ip = int32 + integer, parameter :: string_len = 100 + integer, parameter :: max_name_len = 32 + + ! 基础配置类型 + type :: cfd_config_base + character(len=max_name_len) :: ic_type = "step" + character(len=max_name_len) :: recon_scheme = "eno" + character(len=max_name_len) :: flux_type = "rusanov" + integer(ip) :: rk_order = 1 + real(wp) :: wave_speed = 1.0_wp + real(wp) :: final_time = 0.625_wp + real(wp) :: dt = 0.025_wp + character(len=max_name_len) :: boundary_type = "periodic" + integer(ip) :: spatial_order = 2 + character(len=max_name_len) :: equation_type = "linear_advection" + character(len=max_name_len) :: problem_type = "linear_advection" + logical :: verbose = .true. + end type cfd_config_base + + ! 组件信息类型 + type :: component_info + character(len=max_name_len) :: category = "" + character(len=max_name_len) :: name = "" + integer(ip) :: order = 0 + end type component_info + +end module base_modules \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/base/precision.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/src/base/precision.f90 new file mode 100644 index 00000000..4ac5fd7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/base/precision.f90 @@ -0,0 +1,9 @@ +! src/base/precision.f90(简单版本) +module precision_module + use base_modules, only: wp, ip + implicit none + + ! 重新导出,确保兼容 + public :: wp, ip + +end module precision_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02g/src/core/CMakeLists.txt new file mode 100644 index 00000000..d8b8df06 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/core/CMakeLists.txt @@ -0,0 +1,14 @@ +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") + +add_library(core STATIC + registry.f90 +) + +target_link_libraries(core PRIVATE base) + +set_target_properties(core PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Core module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/core/factory_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/src/core/factory_base.f90 new file mode 100644 index 00000000..302418a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/core/factory_base.f90 @@ -0,0 +1,57 @@ +! src/core/factory_base.f90 +module factory_base_module + use base_modules, only: wp, ip + use registry_module, only: create_component, has_component + + implicit none + private + public :: wp, ip, factory_base, factory_create + + ! 工厂基类 + type :: factory_base + character(len=max_name_length) :: category = "" + contains + procedure :: create => factory_base_create + procedure :: get_available => factory_base_get_available + end type factory_base + + ! 便捷函数类型 + abstract interface + function factory_function_interface(category, name) result(instance) + import :: wp + character(len=*), intent(in) :: category, name + class(*), allocatable :: instance + end function factory_function_interface + end interface + +contains + + ! 创建工厂实例 + function factory_create(category) result(factory) + character(len=*), intent(in) :: category + type(factory_base) :: factory + factory%category = trim(category) + end function factory_create + + ! 工厂创建方法 + function factory_base_create(this, name) result(instance) + class(factory_base), intent(in) :: this + character(len=*), intent(in) :: name + class(*), allocatable :: instance + + instance = create_component(this%category, name) + end function factory_base_create + + ! 获取可用组件列表(简化版) + subroutine factory_base_get_available(this, names, count) + class(factory_base), intent(in) :: this + character(len=*), allocatable, intent(out) :: names(:) + integer(ip), intent(out) :: count + + ! 这里需要实现从注册表获取列表的逻辑 + ! 暂时返回空列表 + count = 0 + allocate(character(len=max_name_length) :: names(0)) + end subroutine factory_base_get_available + +end module factory_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..a960167c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/src/core/registry.f90 new file mode 100644 index 00000000..acc63edb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/core/registry.f90 @@ -0,0 +1,203 @@ +! src/core/registry.f90 +module registry_module + use base_modules, only: wp, ip, max_name_len, component_info + + implicit none + private + + ! 明确公开所有需要的接口 + public :: wp, ip ! 类型参数 + public :: component_info ! 类型 + public :: registry_init, registry_cleanup ! 初始化/清理 + public :: register_component_simple ! 注册组件 + public :: has_component_simple ! 检查组件 + public :: list_components ! 列出组件 + public :: registry_is_initialized ! 检查初始化状态 ← 新增 + public :: registry_get_size ! 获取大小 ← 新增 + + ! 全局注册表 + type :: component_registry + type(component_info), allocatable :: components(:) + integer(ip) :: count = 0 + integer(ip) :: capacity = 100 + logical :: initialized = .false. + logical :: verbose = .true. + end type component_registry + + type(component_registry) :: registry + +contains + + ! ==================== 公共API ==================== + + subroutine registry_init(verbose) + logical, optional, intent(in) :: verbose + + if (registry%initialized) then + if (registry%verbose) then + print *, "[REGISTRY] Already initialized" + end if + return + end if + + if (present(verbose)) then + registry%verbose = verbose + end if + + allocate(registry%components(registry%capacity)) + registry%initialized = .true. + + if (registry%verbose) then + print *, "[REGISTRY] Initialized with capacity:", registry%capacity + end if + end subroutine registry_init + + subroutine registry_cleanup() + if (allocated(registry%components)) then + deallocate(registry%components) + end if + registry%initialized = .false. + registry%count = 0 + + if (registry%verbose) then + print *, "[REGISTRY] Cleaned up" + end if + end subroutine registry_cleanup + + subroutine register_component_simple(category, name, order) + character(len=*), intent(in) :: category, name + integer(ip), optional, intent(in) :: order + + integer(ip) :: i + type(component_info) :: info + + if (.not. registry%initialized) then + call registry_init() + end if + + ! 检查是否已存在 + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + if (registry%verbose) then + print *, "[WARN] Overwriting component: ", trim(category), ".", trim(name) + end if + + ! 更新 + if (present(order)) then + registry%components(i)%order = order + else + registry%components(i)%order = 0 + end if + return + end if + end do + + ! 扩展数组 + if (registry%count >= registry%capacity) then + call expand_registry() + end if + + ! 添加新组件 + registry%count = registry%count + 1 + + info%category = trim(category) + info%name = trim(name) + info%order = 0 + if (present(order)) then + info%order = order + end if + + registry%components(registry%count) = info + + if (registry%verbose) then + print *, "[OK] Registered simple: ", trim(category), ".", trim(name) + end if + end subroutine register_component_simple + + logical function has_component_simple(category, name) + character(len=*), intent(in) :: category, name + + integer(ip) :: i + + has_component_simple = .false. + + if (.not. registry%initialized) return + + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + has_component_simple = .true. + return + end if + end do + end function has_component_simple + + subroutine list_components(category) + character(len=*), optional, intent(in) :: category + + integer(ip) :: i, count + + if (.not. registry%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + if (registry%count == 0) then + print *, "[INFO] No components registered" + return + end if + + count = 0 + print *, "=== Registry Contents ===" + do i = 1, registry%count + if (.not. present(category) .or. & + trim(registry%components(i)%category) == trim(category)) then + call print_component_info(registry%components(i)) + count = count + 1 + end if + end do + + print *, "Total:", count, "components" + print *, "==========================" + end subroutine list_components + + ! ==================== 新增函数 ==================== + + logical function registry_is_initialized() + ! 检查注册表是否已初始化 + registry_is_initialized = registry%initialized + end function registry_is_initialized + + integer(ip) function registry_get_size() + ! 获取注册表中的组件数量 + registry_get_size = registry%count + end function registry_get_size + + ! ==================== 内部辅助函数 ==================== + + subroutine expand_registry() + type(component_info), allocatable :: temp(:) + + registry%capacity = registry%capacity * 2 + allocate(temp(registry%capacity)) + temp(1:registry%count) = registry%components(1:registry%count) + call move_alloc(temp, registry%components) + + if (registry%verbose) then + print *, "[INFO] Registry expanded to capacity:", registry%capacity + end if + end subroutine expand_registry + + subroutine print_component_info(info) + type(component_info), intent(in) :: info + + if (info%order > 0) then + print *, " [", trim(info%category), ".", trim(info%name), & + " (order:", info%order, ")]" + else + print *, " [", trim(info%category), ".", trim(info%name), "]" + end if + end subroutine print_component_info + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02g/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..70cbbd2f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/infrastructure/CMakeLists.txt @@ -0,0 +1,17 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "Configuring infrastructure module...") + +add_library(infrastructure STATIC + config.f90 + mesh.f90 + domain.f90 # 新增 + solution.f90 # 新增 +) + +target_link_libraries(infrastructure PRIVATE base) + +set_target_properties(infrastructure PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Infrastructure module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/src/infrastructure/config.f90 new file mode 100644 index 00000000..719e14d2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/infrastructure/config.f90 @@ -0,0 +1,64 @@ +! src/infrastructure/config.f90 +module config_module + use base_modules, only: wp, ip, max_name_len, cfd_config_base + + implicit none + public :: wp, ip, cfd_config, config_print, config_with_reconstruction + + ! 扩展配置类型 + type, extends(cfd_config_base) :: cfd_config + real(wp) :: left_boundary_value = 1.0_wp + real(wp) :: right_boundary_value = 2.0_wp + real(wp) :: domain_length = 2.0_wp + end type cfd_config + +contains + + subroutine config_print(cfg) + type(cfd_config), intent(in) :: cfg + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(cfg%ic_type) + print *, "Reconstruction: ", trim(cfg%recon_scheme), " (order:", cfg%spatial_order, ")" + print *, "Flux type: ", trim(cfg%flux_type) + print *, "Time integration: RK", cfg%rk_order + print *, "Wave speed: ", cfg%wave_speed + print *, "Final time: ", cfg%final_time + print *, "Time step: ", cfg%dt + print *, "Boundary: ", trim(cfg%boundary_type) + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(cfg, scheme, order) + type(cfd_config), intent(inout) :: cfg + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + integer :: i + + ! 转换为小写 + cfg%recon_scheme = scheme + do i = 1, len_trim(cfg%recon_scheme) + if (cfg%recon_scheme(i:i) >= 'A' .and. cfg%recon_scheme(i:i) <= 'Z') then + cfg%recon_scheme(i:i) = char(ichar(cfg%recon_scheme(i:i)) + 32) + end if + end do + + ! 设置阶数 + if (present(order)) then + cfg%spatial_order = order + else + if (index(cfg%recon_scheme, 'weno') > 0) then + cfg%spatial_order = 5 + else if (trim(cfg%recon_scheme) == 'eno') then + cfg%spatial_order = 3 + end if + end if + + if (cfg%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(cfg%recon_scheme), & + " Order: ", cfg%spatial_order + end if + end subroutine config_with_reconstruction + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/infrastructure/domain.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/src/infrastructure/domain.f90 new file mode 100644 index 00000000..c3662f03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/infrastructure/domain.f90 @@ -0,0 +1,102 @@ +! src/infrastructure/domain.f90 +module domain_module + use base_modules, only: wp, ip, max_name_len + use config_module, only: cfd_config + use mesh_module, only: mesh_type + + implicit none + private + public :: wp, ip, domain_type, domain_create, is_physical_cell + + type :: domain_type + type(cfd_config), pointer :: config => null() + type(mesh_type), pointer :: mesh => null() + integer(ip) :: nghosts = 0 + integer(ip) :: ist = 1 ! 物理区域起始索引(1-based) + integer(ip) :: ied = 1 ! 物理区域结束索引(exclusive) + integer(ip) :: ntcells = 0 ! 总单元数(含ghost) + contains + procedure :: print_info => domain_print_info + procedure :: get_physical_indices => domain_get_physical_indices + end type domain_type + +contains + + function domain_create(config, mesh) result(domain) + type(cfd_config), target, intent(in) :: config + type(mesh_type), target, intent(in) :: mesh + type(domain_type) :: domain + + domain%config => config + domain%mesh => mesh + + ! 计算ghost层数(参考Julia的_calc_nghosts) + domain%nghosts = calc_nghosts(config) + domain%ist = domain%nghosts + 1 + domain%ied = domain%ist + mesh%ncells + domain%ntcells = mesh%ncells + 2 * domain%nghosts + + if (config%verbose) then + print *, "[DOMAIN] Created:" + print *, " Ghost layers: ", domain%nghosts + print *, " Physical cells: ", domain%ist, " to ", domain%ied - 1 + print *, " Total cells: ", domain%ntcells + end if + end function domain_create + + function calc_nghosts(config) result(nghosts) + type(cfd_config), intent(in) :: config + integer(ip) :: nghosts + + character(len=max_name_len) :: scheme + + scheme = config%recon_scheme + + if (scheme == "eno") then + nghosts = config%spatial_order + else if (index(scheme, "weno") > 0) then + nghosts = config%spatial_order / 2 + 1 + else + print *, "[WARNING] Unknown scheme, using default nghosts=2" + nghosts = 2 + end if + + if (nghosts <= 0) then + print *, "[ERROR] Invalid nghosts: ", nghosts + nghosts = 2 + end if + end function calc_nghosts + + logical function is_physical_cell(this, idx) + class(domain_type), intent(in) :: this + integer(ip), intent(in) :: idx + is_physical_cell = (idx >= this%ist .and. idx < this%ied) + end function is_physical_cell + + function domain_get_physical_indices(this) result(indices) + class(domain_type), intent(in) :: this + integer(ip), allocatable :: indices(:) + integer(ip) :: i, count + + count = this%ied - this%ist + allocate(indices(count)) + + do i = 1, count + indices(i) = this%ist + i - 1 + end do + end function domain_get_physical_indices + + subroutine domain_print_info(this) + class(domain_type), intent(in) :: this + + print *, "=== Domain Information ===" + print *, "Configuration: ", trim(this%config%recon_scheme), & + " order ", this%config%spatial_order + print *, "Ghost layers: ", this%nghosts + print *, "Physical cells: ", this%ist, " to ", this%ied - 1 + print *, "Total cells: ", this%ntcells + print *, "Mesh cells: ", this%mesh%ncells + print *, "==========================" + end subroutine domain_print_info + +end module domain_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..f810f3a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/infrastructure/mesh.f90 @@ -0,0 +1,73 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use base_modules, only: wp, ip + + implicit none + public :: wp, ip, mesh_type, mesh_init, mesh_print_info + + ! 网格类型 + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer(ip) :: ncells = 40 + integer(ip) :: nnodes + integer(ip) :: nx + real(wp), allocatable :: x(:) ! 节点坐标 + real(wp), allocatable :: xcc(:) ! 单元中心坐标 + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer(ip), optional, intent(in) :: ncells + + integer(ip) :: i + + ! 设置参数 + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! 计算 + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! 分配内存 + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! 生成节点坐标 + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! 生成单元中心坐标 + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== Mesh Information ===" + print *, "Domain: [", this%xmin, ", ", this%xmax, "]" + print *, "Cells: ", this%ncells + print *, "Nodes: ", this%nnodes + print *, "dx: ", this%dx + print *, "L: ", this%L + print *, "========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/infrastructure/solution.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/src/infrastructure/solution.f90 new file mode 100644 index 00000000..ce88fd8a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/infrastructure/solution.f90 @@ -0,0 +1,131 @@ +! src/infrastructure/solution.f90 +module solution_module + use base_modules, only: wp, ip + use domain_module, only: domain_type + + implicit none + private + public :: wp, ip, solution_type, solution_create, solution_reset + + type :: solution_type + type(domain_type), pointer :: domain => null() + real(wp), allocatable :: u(:) ! 当前解(含ghost) + real(wp), allocatable :: un(:) ! 旧解 + real(wp), allocatable :: q_face_left(:) ! 左界面值 + real(wp), allocatable :: q_face_right(:)! 右界面值 + real(wp), allocatable :: flux(:) ! 通量 + real(wp), allocatable :: res(:) ! 残差 + contains + procedure :: initialize => solution_initialize + procedure :: update_old_field => solution_update_old_field + procedure :: print_info => solution_print_info + procedure :: reset => solution_reset_instance + end type solution_type + +contains + + function solution_create(domain) result(solution) + type(domain_type), target, intent(in) :: domain + type(solution_type) :: solution + + integer(ip) :: ncells, nnodes, ntcells + + solution%domain => domain + + ncells = domain%mesh%ncells + nnodes = domain%mesh%nnodes + ntcells = domain%ntcells + + ! 分配数组(与Julia solution.jl一致) + allocate(solution%u(ntcells), source=0.0_wp) + allocate(solution%un(ntcells), source=0.0_wp) + allocate(solution%q_face_left(nnodes), source=0.0_wp) + allocate(solution%q_face_right(nnodes), source=0.0_wp) + allocate(solution%flux(nnodes), source=0.0_wp) + allocate(solution%res(ncells), source=0.0_wp) + + if (domain%config%verbose) then + print *, "[SOLUTION] Created:" + print *, " u size: ", size(solution%u), " (with ghosts)" + print *, " flux size: ", size(solution%flux) + print *, " res size: ", size(solution%res) + end if + end function solution_create + + subroutine solution_initialize(this, initial_values) + class(solution_type), intent(inout) :: this + real(wp), intent(in), optional :: initial_values(:) + + integer(ip) :: i, idx + type(domain_type), pointer :: domain + + domain => this%domain + + if (present(initial_values)) then + ! 应用初始值到物理区域 + do i = domain%ist, domain%ied - 1 + idx = i - domain%ist + 1 + if (idx <= size(initial_values)) then + this%u(i) = initial_values(idx) + end if + end do + else + ! 默认为0 + this%u = 0.0_wp + end if + + ! 同步旧场(与Julia的update_old_field一致) + call this%update_old_field() + + if (domain%config%verbose) then + print *, "[SOLUTION] Initialized" + print *, " u range: ", minval(this%u), " to ", maxval(this%u) + end if + end subroutine solution_initialize + + subroutine solution_update_old_field(this) + class(solution_type), intent(inout) :: this + this%un = this%u ! 与Julia的 un .= u 一致 + end subroutine solution_update_old_field + + subroutine solution_reset_instance(this) + class(solution_type), intent(inout) :: this + call solution_reset(this) + end subroutine solution_reset_instance + + subroutine solution_reset(solution) + type(solution_type), intent(inout) :: solution + + if (allocated(solution%u)) solution%u = 0.0_wp + if (allocated(solution%un)) solution%un = 0.0_wp + if (allocated(solution%q_face_left)) solution%q_face_left = 0.0_wp + if (allocated(solution%q_face_right)) solution%q_face_right = 0.0_wp + if (allocated(solution%flux)) solution%flux = 0.0_wp + if (allocated(solution%res)) solution%res = 0.0_wp + + if (associated(solution%domain) .and. solution%domain%config%verbose) then + print *, "[SOLUTION] Reset" + end if + end subroutine solution_reset + + subroutine solution_print_info(this) + class(solution_type), intent(in) :: this + + print *, "=== Solution Information ===" + print *, "Arrays:" + print *, " u: ", size(this%u), " elements" + print *, " un: ", size(this%un), " elements" + print *, " q_face_left: ", size(this%q_face_left), " elements" + print *, " q_face_right: ", size(this%q_face_right), " elements" + print *, " flux: ", size(this%flux), " elements" + print *, " res: ", size(this%res), " elements" + + if (allocated(this%u)) then + print *, "Values:" + print *, " u min/max: ", minval(this%u), maxval(this%u) + print *, " un min/max: ", minval(this%un), maxval(this%un) + end if + print *, "============================" + end subroutine solution_print_info + +end module solution_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/manager/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02g/src/manager/CMakeLists.txt new file mode 100644 index 00000000..00c8bf49 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/manager/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/manager/CMakeLists.txt +message(STATUS "配置管理器模块...") + +# 创建管理器库 +add_library(manager STATIC + component_manager.f90 + component_factory.f90 +) + +# 明确依赖关系:管理器依赖所有其他模块 +target_link_libraries(manager + PRIVATE + core + infrastructure + reconstructor + flux +) + +# 设置模块输出目录 +set_target_properties(manager PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "管理器模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/manager/component_factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/src/manager/component_factory.f90 new file mode 100644 index 00000000..114fedea --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/manager/component_factory.f90 @@ -0,0 +1,127 @@ +! src/manager/component_factory.f90 +module component_factory_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + use eno_reconstructor_module, only: eno_reconstructor, create_eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor, create_weno3_reconstructor + use rusanov_flux_module, only: rusanov_flux, create_rusanov_flux + + implicit none + private + public :: wp, create_reconstructor, create_flux_calculator + + ! 错误代码 + integer, parameter :: CM_SUCCESS = 0 + integer, parameter :: CM_ERROR_UNKNOWN_SCHEME = 1 + integer, parameter :: CM_ERROR_UNKNOWN_FLUX = 2 + integer, parameter :: CM_ERROR_INVALID_ORDER = 3 + +contains + + ! ==================== 重构器创建 ==================== + + function create_reconstructor(config, status) result(recon) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(reconstructor_base), allocatable :: recon + + character(len=20) :: scheme + integer :: order, error_code + + scheme = trim(adjustl(config%recon_scheme)) + order = config%spatial_order + + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating reconstructor: ", scheme, " order=", order + end if + + select case(scheme) + case('eno') + allocate(eno_reconstructor :: recon) + select type(recon) + type is(eno_reconstructor) + recon = create_eno_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case('weno3') + allocate(weno3_reconstructor :: recon) + select type(recon) + type is(weno3_reconstructor) + recon = create_weno3_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case default + error_code = CM_ERROR_UNKNOWN_SCHEME + if (config%verbose) then + print *, "[ERROR] Unknown reconstructor scheme: ", scheme + print *, " Available: eno, weno3" + end if + end select + + ! 检查阶数有效性 + if (error_code == CM_SUCCESS) then + if (order < 1) then + error_code = CM_ERROR_INVALID_ORDER + if (config%verbose) then + print *, "[ERROR] Invalid spatial order: ", order + end if + end if + end if + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Reconstructor creation failed" + end if + end function create_reconstructor + + ! ==================== 通量计算器创建 ==================== + + function create_flux_calculator(config, status) result(flux) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(flux_calculator_base), allocatable :: flux + + character(len=20) :: flux_type + integer :: error_code + + flux_type = trim(adjustl(config%flux_type)) + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating flux calculator: ", flux_type + end if + + select case(flux_type) + case('rusanov') + allocate(rusanov_flux :: flux) + select type(flux) + type is(rusanov_flux) + flux = create_rusanov_flux() + flux%wave_speed_default = config%wave_speed + end select + + case default + error_code = CM_ERROR_UNKNOWN_FLUX + if (config%verbose) then + print *, "[ERROR] Unknown flux type: ", flux_type + print *, " Available: rusanov" + end if + end select + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Flux calculator creation failed" + end if + end function create_flux_calculator + +end module component_factory_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/manager/component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/src/manager/component_manager.f90 new file mode 100644 index 00000000..9e095c25 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/manager/component_manager.f90 @@ -0,0 +1,75 @@ +! src/manager/component_manager.f90 +module component_manager_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use component_factory_module, only: create_reconstructor, create_flux_calculator + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + implicit none + private + public :: wp, component_manager_info, validate_config + public :: create_reconstructor, create_flux_calculator + +contains + + ! ==================== 配置验证 ==================== + + function validate_config(config) result(is_valid) + type(cfd_config), intent(in) :: config + logical :: is_valid + + integer :: status + class(reconstructor_base), allocatable :: test_recon + class(flux_calculator_base), allocatable :: test_flux + + is_valid = .false. + + ! 测试创建重构器 + test_recon = create_reconstructor(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid reconstructor configuration" + end if + return + end if + + ! 测试创建通量计算器 + test_flux = create_flux_calculator(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid flux configuration" + end if + return + end if + + ! 清理测试组件 + if (allocated(test_recon)) deallocate(test_recon) + if (allocated(test_flux)) deallocate(test_flux) + + is_valid = .true. + + if (config%verbose) then + print *, "[CONFIG VALIDATION] Configuration is valid" + end if + end function validate_config + + ! ==================== 信息显示 ==================== + + subroutine component_manager_info() + print *, "=== Component Manager ===" + print *, "Available reconstructors:" + print *, " - eno (orders: 1-7)" + print *, " - weno3 (order: 3)" + print *, "" + print *, "Available flux calculators:" + print *, " - rusanov" + print *, "" + print *, "Features:" + print *, " - Configuration validation" + print *, " - Component creation from config" + print *, " - Error handling with status codes" + print *, "=========================" + end subroutine component_manager_info + +end module component_manager_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02g/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02g/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..3fde7746 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,28 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux STATIC + base.f90 + rusanov.f90 +) + +#target_link_libraries(flux PRIVATE core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 明确设置不使用 /Qm64 选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + set_target_properties(flux PROPERTIES + Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /Qm64" # 明确设置,避免继承问题 + ) +endif() + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/src/numerics/flux/base.f90 new file mode 100644 index 00000000..7080a7ae --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/numerics/flux/base.f90 @@ -0,0 +1,30 @@ +!src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + procedure :: print_basic_info => flux_print_basic ! 添加辅助方法 + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + + subroutine flux_print_basic(this) + class(flux_calculator_base), intent(in) :: this + print *, " Name: ", trim(this%name) + end subroutine flux_print_basic + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..daa9e3bb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/numerics/flux/rusanov.f90 @@ -0,0 +1,41 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux, create_rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + + ! 构造函数接口 + interface rusanov_flux + module procedure create_rusanov_flux + end interface + +contains + + ! 构造函数 + type(rusanov_flux) function create_rusanov_flux() result(this) + this%name = "Rusanov" + this%wave_speed_default = 1.0_real64 + end function create_rusanov_flux + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Flux calculator information:" + call this%print_basic_info() + + ! 添加Rusanov特有信息 + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02g/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..5e4b938d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,22 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor STATIC + base.f90 + eno.f90 + weno3.f90 +) + +target_link_libraries(reconstructor PRIVATE core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..53798d02 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/numerics/reconstructor/base.f90 @@ -0,0 +1,33 @@ +!src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + procedure :: print_basic_info => reconstructor_print_basic ! 添加一个辅助方法 + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + + subroutine reconstructor_print_basic(this) + class(reconstructor_base), intent(in) :: this + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_print_basic + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..f973e8b3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor, create_eno_reconstructor ! ← 添加这个 + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + + ! 构造函数接口 + interface eno_reconstructor + module procedure create_eno_reconstructor + end interface + +contains + + ! 构造函数 + type(eno_reconstructor) function create_eno_reconstructor() result(this) + this%name = "ENO" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_eno_reconstructor + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加ENO特有信息 + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..d5b7a747 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno3_reconstructor, create_weno3_reconstructor + + type, extends(reconstructor_base) :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + + ! 构造函数接口 + interface weno3_reconstructor + module procedure create_weno3_reconstructor + end interface + +contains + + ! 构造函数 + type(weno3_reconstructor) function create_weno3_reconstructor() result(this) + this%name = "WENO3" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_weno3_reconstructor + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO3特有信息 + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/physics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02g/src/physics/CMakeLists.txt new file mode 100644 index 00000000..cc4e233a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/physics/CMakeLists.txt @@ -0,0 +1,19 @@ +# src/physics/CMakeLists.txt +message(STATUS "配置物理模块...") + +# 创建物理模块库 +add_library(physics STATIC + physics_interface.f90 + equations/linear_convection.f90 + problems/linear_convection_problem.f90 +) + +# 链接依赖 +target_link_libraries(physics PRIVATE base) + +# 设置模块输出目录 +set_target_properties(physics PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "物理模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/physics/equations/linear_convection.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/src/physics/equations/linear_convection.f90 new file mode 100644 index 00000000..595a2110 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/physics/equations/linear_convection.f90 @@ -0,0 +1,83 @@ +! src/physics/equations/linear_convection.f90 +module linear_convection_equation + use precision_module, only: wp, ip + use physics_interface, only: physics_equation + implicit none + private + + public :: linear_convection_eq, create_linear_convection_eq + public :: linear_convection_flux, linear_convection_speed + + ! 具体方程类型 + type, extends(physics_equation) :: linear_convection_eq + real(wp) :: wave_speed = 1.0_wp + contains + procedure :: flux => lc_flux + procedure :: speed => lc_speed + end type linear_convection_eq + + ! 独立函数(供外部调用) + interface linear_convection_flux + module procedure :: lc_flux_func + end interface + + interface linear_convection_speed + module procedure :: lc_speed_func + end interface + +contains + + ! 构造函数 + function create_linear_convection_eq(wave_speed) result(eq) + real(wp), intent(in), optional :: wave_speed + type(linear_convection_eq) :: eq + + eq%name = "Linear Convection" + if (present(wave_speed)) then + eq%wave_speed = wave_speed + else + eq%wave_speed = 1.0_wp + end if + end function create_linear_convection_eq + + ! 方法实现 + pure function lc_flux(this, u) result(f) + class(linear_convection_eq), intent(in) :: this + real(wp), intent(in) :: u + real(wp) :: f + f = this%wave_speed * u + end function lc_flux + + pure function lc_speed(this) result(a) + class(linear_convection_eq), intent(in) :: this + real(wp) :: a + a = this%wave_speed + end function lc_speed + + ! 独立函数 + pure function lc_flux_func(u, wave_speed) result(f) + real(wp), intent(in) :: u + real(wp), intent(in), optional :: wave_speed + real(wp) :: f + real(wp) :: a + + if (present(wave_speed)) then + a = wave_speed + else + a = 1.0_wp + end if + f = a * u + end function lc_flux_func + + pure function lc_speed_func(wave_speed) result(a) + real(wp), intent(in), optional :: wave_speed + real(wp) :: a + + if (present(wave_speed)) then + a = wave_speed + else + a = 1.0_wp + end if + end function lc_speed_func + +end module linear_convection_equation \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/physics/physics_interface.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/src/physics/physics_interface.f90 new file mode 100644 index 00000000..55662e5c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/physics/physics_interface.f90 @@ -0,0 +1,64 @@ +! src/physics/physics_interface.f90 +module physics_interface + use precision_module, only: wp, ip + implicit none + private + + ! 定义抽象基类型 + type, abstract :: physics_equation + character(len=:), allocatable :: name + contains + procedure(eq_flux_abs), deferred :: flux + procedure(eq_speed_abs), deferred :: speed + end type physics_equation + + type, abstract :: physics_problem + character(len=:), allocatable :: name + contains + procedure(prob_ic_abs), deferred :: initial_condition + procedure(prob_bc_abs), deferred :: boundary_condition + procedure(prob_exact_abs), deferred :: exact_solution + end type physics_problem + + ! 抽象接口定义 + abstract interface + pure function eq_flux_abs(this, u) result(f) + import :: physics_equation, wp + class(physics_equation), intent(in) :: this + real(wp), intent(in) :: u + real(wp) :: f + end function eq_flux_abs + + pure function eq_speed_abs(this) result(a) + import :: physics_equation, wp + class(physics_equation), intent(in) :: this + real(wp) :: a + end function eq_speed_abs + + subroutine prob_ic_abs(this, x, u) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp), intent(out) :: u(:) + end subroutine prob_ic_abs + + subroutine prob_bc_abs(this, u, t) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(inout) :: u(:) + real(wp), intent(in), optional :: t + end subroutine prob_bc_abs + + function prob_exact_abs(this, x, t) result(u) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(in) :: x(:), t + real(wp), dimension(size(x)) :: u + end function prob_exact_abs + end interface + + ! 公开接口 + public :: physics_equation, physics_problem + public :: wp, ip + +end module physics_interface \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/src/physics/problems/linear_convection_problem.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/src/physics/problems/linear_convection_problem.f90 new file mode 100644 index 00000000..31210710 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/src/physics/problems/linear_convection_problem.f90 @@ -0,0 +1,116 @@ +! src/physics/problems/linear_convection_problem.f90 +module linear_convection_problem + use precision_module, only: wp, ip + use physics_interface, only: physics_problem + implicit none + private + + public :: linear_convection_prob, create_linear_convection_prob + + ! 具体问题类型 + type, extends(physics_problem) :: linear_convection_prob + real(wp) :: wave_speed = 1.0_wp + real(wp) :: domain_length = 2.0_wp + character(len=20) :: ic_type = "step" + character(len=20) :: boundary_type = "periodic" + contains + procedure :: initial_condition => lc_initial_condition + procedure :: boundary_condition => lc_boundary_condition + procedure :: exact_solution => lc_exact_solution + end type linear_convection_prob + +contains + + ! 构造函数 + function create_linear_convection_prob(wave_speed, domain_length, & + ic_type, boundary_type) result(prob) + real(wp), intent(in), optional :: wave_speed, domain_length + character(len=*), intent(in), optional :: ic_type, boundary_type + type(linear_convection_prob) :: prob + + prob%name = "Linear Convection Problem" + + if (present(wave_speed)) prob%wave_speed = wave_speed + if (present(domain_length)) prob%domain_length = domain_length + if (present(ic_type)) prob%ic_type = ic_type + if (present(boundary_type)) prob%boundary_type = boundary_type + end function create_linear_convection_prob + + ! 初始条件 + subroutine lc_initial_condition(this, x, u) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp), intent(out) :: u(:) + + integer :: i + + select case (trim(this%ic_type)) + case ("step") + do i = 1, size(x) + if (x(i) >= 0.5_wp .and. x(i) <= 1.0_wp) then + u(i) = 2.0_wp + else + u(i) = 1.0_wp + end if + end do + + case ("sin", "sine") + do i = 1, size(x) + u(i) = sin(2.0_wp * 3.141592653589793_wp * x(i) / this%domain_length) + end do + + case ("gaussian") + do i = 1, size(x) + u(i) = exp(-((x(i) - 0.5_wp) / 0.1_wp)**2) + end do + + case default + ! 默认阶跃函数 + do i = 1, size(x) + if (x(i) >= 0.5_wp .and. x(i) <= 1.0_wp) then + u(i) = 2.0_wp + else + u(i) = 1.0_wp + end if + end do + end select + end subroutine lc_initial_condition + + ! 边界条件(虚拟实现,实际在boundary模块) + subroutine lc_boundary_condition(this, u, t) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(inout) :: u(:) + real(wp), intent(in), optional :: t + + ! 边界条件将在独立模块实现 + print *, "[PROBLEM] Boundary condition placeholder" + if (present(t)) then + print *, " Time = ", t + end if + end subroutine lc_boundary_condition + + ! 精确解(周期性平移) + function lc_exact_solution(this, x, t) result(u) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(in) :: x(:), t + real(wp), dimension(size(x)) :: u + real(wp), dimension(size(x)) :: x_shifted + integer :: i + + ! 周期性平移 + do i = 1, size(x) + x_shifted(i) = x(i) - this%wave_speed * t + ! 确保在 [0, domain_length) 范围内 + do while (x_shifted(i) < 0.0_wp) + x_shifted(i) = x_shifted(i) + this%domain_length + end do + do while (x_shifted(i) >= this%domain_length) + x_shifted(i) = x_shifted(i) - this%domain_length + end do + end do + + ! 重用初始条件函数 + call this%initial_condition(x_shifted, u) + end function lc_exact_solution + +end module linear_convection_problem \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02g/tests/CMakeLists.txt new file mode 100644 index 00000000..01e0a2bd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/tests/CMakeLists.txt @@ -0,0 +1,85 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +# +#message(STATUS "CMAKE_Fortran_MODULE_DIRECTORY=${CMAKE_Fortran_MODULE_DIRECTORY}") +# +add_executable(test_minimal_simple test_minimal_simple.f90) +target_link_libraries(test_minimal_simple + PRIVATE + core # 必须链接core库 + infrastructure +) + + +add_executable(test_simple_link test_simple_link.f90) +target_link_libraries(test_simple_link + PRIVATE + reconstructor + flux +) + + +add_executable(test_factory_simple test_factory_simple.f90) +target_link_libraries(test_factory_simple + PRIVATE + core + infrastructure + reconstructor + flux +) + + +add_executable(test_component_manager test_component_manager.f90) + +target_link_libraries(test_component_manager + PRIVATE + manager # ← 链接到新的管理器库 + infrastructure +) +# +#add_executable(test_architecture test_architecture.f90) +#target_link_libraries(test_architecture +# PRIVATE +# core +# infrastructure +# manager +#) + +#add_executable(test_solver_framework test_solver_framework.f90) +#target_link_libraries(test_solver_framework +# PRIVATE +# core +# infrastructure +# manager +#) +# +#add_executable(test_cfd_architecture test_cfd_architecture.f90) +#target_link_libraries(test_cfd_architecture +# PRIVATE +# base +# core +# infrastructure +#) + + +add_executable(test_basic_only test_basic_only.f90) +target_link_libraries(test_basic_only + PRIVATE + infrastructure + core +) + +add_executable(test_physics_minimal test_physics_minimal.f90) +target_link_libraries(test_physics_minimal + PRIVATE + physics + base +) + +add_executable(test_domain_solution test_domain_solution.f90) +target_link_libraries(test_domain_solution + PRIVATE + infrastructure + core +) diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_basic_only.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_basic_only.f90 new file mode 100644 index 00000000..20901ddc --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_basic_only.f90 @@ -0,0 +1,56 @@ +! tests/test_basic_only.f90 +program test_basic_only + ! 只测试最基本的功能,不依赖复杂模块 + use config_module, only: cfd_config, config_print, wp + use mesh_module, only: mesh_type + use registry_module, only: registry_init, registry_cleanup, & + register_component_simple, list_components + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "=== BASIC TEST - Minimal Functionality ===" + print *, "" + + ! 测试1: 配置 + print *, "1. Testing configuration..." + print *, "----------------------------" + call config_print(config) + print *, "" + + ! 测试2: 网格 + print *, "2. Testing mesh..." + print *, "------------------" + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=5) + print *, "Mesh initialized:" + print *, " Cells: ", mesh%ncells + print *, " Nodes: ", mesh%nnodes + print *, " dx: ", mesh%dx + print *, "" + + ! 测试3: 注册系统 + print *, "3. Testing registry..." + print *, "----------------------" + + call registry_init() + + ! 注册组件(使用简化版本) + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! 列出组件 + call list_components() + print *, "" + + ! 清理 + call registry_cleanup() + + print *, "=== TEST PASSED ===" + print *, "✓ Configuration works" + print *, "✓ Mesh works" + print *, "✓ Registry works" + +end program test_basic_only \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_cfd_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_cfd_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_cfd_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_component_manager.f90 new file mode 100644 index 00000000..f60c3505 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_component_manager.f90 @@ -0,0 +1,111 @@ +! tests/test_component_manager.f90 +program test_component_manager + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use component_manager_module, only: create_reconstructor, create_flux_calculator + use component_manager_module, only: component_manager_info, validate_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + implicit none + + type(cfd_config) :: config + class(reconstructor_base), allocatable :: recon + class(flux_calculator_base), allocatable :: flux + integer :: status + logical :: is_valid + + print *, "=== Component Manager Test ===" + print *, "" + + ! 显示组件管理器信息 + call component_manager_info() + print *, "" + + ! 测试1: 基本配置 + print *, "1. Testing basic ENO3 + Rusanov configuration..." + print *, "-----------------------------------------------" + + config%verbose = .true. + call config_print(config) + + ! 配置ENO3重构 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + call config_print(config) + print *, "" + + ! 验证配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Configuration is valid" + else + print *, "[ERROR] Configuration is invalid" + end if + print *, "" + + ! 测试2: 创建组件 + print *, "2. Testing component creation..." + print *, "--------------------------------" + + ! 创建重构器(带状态检查) + recon = create_reconstructor(config, status) + if (status == 0) then + print *, "[OK] Reconstructor created successfully" + call recon%info() + else + print *, "[ERROR] Failed to create reconstructor, code:", status + end if + print *, "" + + ! 创建通量计算器 + flux = create_flux_calculator(config, status) + if (status == 0) then + print *, "[OK] Flux calculator created successfully" + call flux%info() + else + print *, "[ERROR] Failed to create flux calculator, code:", status + end if + print *, "" + + ! 测试3: WENO3重构测试 + print *, "3. Testing WENO3 configuration..." + print *, "---------------------------------" + + call config_with_reconstruction(config, "weno3", 3) + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] WENO3 configuration is valid" + + ! 创建WENO3重构器 + recon = create_reconstructor(config) + call recon%info() + else + print *, "[ERROR] WENO3 configuration is invalid" + end if + print *, "" + + ! 测试4: 错误配置测试 + print *, "4. Testing invalid configuration..." + print *, "-----------------------------------" + + config%recon_scheme = "unknown_scheme" + config%flux_type = "unknown_flux" + + is_valid = validate_config(config) + if (.not. is_valid) then + print *, "[OK] Invalid configuration correctly rejected" + else + print *, "[ERROR] Invalid configuration should have been rejected" + end if + + ! 清理 + if (allocated(recon)) deallocate(recon) + if (allocated(flux)) deallocate(flux) + + print *, "" + print *, "=== Component manager test completed successfully ===" + +end program test_component_manager \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_domain_solution.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_domain_solution.f90 new file mode 100644 index 00000000..ff659bac --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_domain_solution.f90 @@ -0,0 +1,102 @@ +! tests/test_domain_solution.f90 +program test_domain_solution + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create, solution_reset + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + real(wp), allocatable :: initial_values(:) + integer :: i + + print *, "=== Domain and Solution Test ===" + print *, "" + + ! 测试1: 不同重构方案的ghost层计算 + print *, "1. Testing ghost layer calculation..." + print *, "--------------------------------------" + + ! ENO3 + call config_with_reconstruction(config, "eno", 3) + config%verbose = .false. + call mesh%init(ncells=10) + domain = domain_create(config, mesh) + print *, "ENO3: nghosts = ", domain%nghosts, " (expected: 3)" + + ! WENO3 + call config_with_reconstruction(config, "weno3", 3) + domain = domain_create(config, mesh) + print *, "WENO3: nghosts = ", domain%nghosts, " (expected: 2)" + + ! WENO5 + call config_with_reconstruction(config, "weno", 5) + domain = domain_create(config, mesh) + print *, "WENO5: nghosts = ", domain%nghosts, " (expected: 3)" + print *, "" + + ! 测试2: Solution数组 + print *, "2. Testing solution arrays..." + print *, "------------------------------" + + call config_with_reconstruction(config, "eno", 3) + config%verbose = .true. + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + domain = domain_create(config, mesh) + call domain%print_info() + print *, "" + + solution = solution_create(domain) + call solution%print_info() + print *, "" + + ! 测试3: 初始化和更新 + print *, "3. Testing initialization and update..." + print *, "----------------------------------------" + + allocate(initial_values(mesh%ncells)) + do i = 1, mesh%ncells + initial_values(i) = sin(2.0_wp * 3.14159265358979_wp * mesh%xcc(i) / mesh%L) + end do + + call solution%initialize(initial_values) + print *, "After initialization:" + print *, " u range: ", minval(solution%u), " to ", maxval(solution%u) + print *, " un range: ", minval(solution%un), " to ", maxval(solution%un) + + ! 修改当前解,测试更新 + solution%u = solution%u * 2.0_wp + call solution%update_old_field() + print *, "After update: max|u - un| = ", maxval(abs(solution%u - solution%un)) + print *, "" + + ! 测试4: 重置 + print *, "4. Testing reset..." + print *, "-------------------" + + call solution_reset(solution) + print *, "After reset:" + print *, " u max: ", maxval(abs(solution%u)) + print *, " un max: ", maxval(abs(solution%un)) + print *, " flux max: ", maxval(abs(solution%flux)) + print *, "" + + deallocate(initial_values) + + print *, "=== Test Summary ===" + print *, "✓ Ghost layer calculation works" + print *, "✓ Domain creation works" + print *, "✓ Solution arrays work" + print *, "✓ Initialization works" + print *, "✓ Field update works" + print *, "✓ Reset works" + print *, "" + print *, "Ready for next step: Implementing Physics modules" + +end program test_domain_solution \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_factory_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_factory_simple.f90 new file mode 100644 index 00000000..db65da7c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_factory_simple.f90 @@ -0,0 +1,58 @@ +! tests/test_factory_simple.f90 (修复版) +program test_factory_simple + use base_modules, only: wp ! ← 添加这行 + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Basic systems + print *, "1. Testing basic systems..." + print *, "-----------------------------" + call config_print(config) + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Creating reconstructors + print *, "2. Testing reconstructors..." + print *, "------------------------------" + + ! 创建并测试ENO重构器 + print *, "Creating ENO reconstructor..." + eno = eno_reconstructor() ! 使用构造函数 + call eno%info() ! 必须调用info方法 + + print *, "" + print *, "Creating WENO3 reconstructor..." + weno3 = weno3_reconstructor() ! 使用构造函数 + call weno3%info() ! 必须调用info方法 + print *, "" + + ! Test 3: Creating flux calculator + print *, "3. Testing flux calculator..." + print *, "-------------------------------" + + print *, "Creating Rusanov flux calculator..." + rusanov = rusanov_flux() ! 使用构造函数 + call rusanov%info() ! 必须调用info方法 + print *, "" + + print *, "=== Factory pattern simple test completed successfully ===" + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_minimal_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_minimal_simple.f90 new file mode 100644 index 00000000..ec03ccf8 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_minimal_simple.f90 @@ -0,0 +1,87 @@ +! tests/test_minimal_simple.f90 +program test_minimal_simple + use base_modules, only: wp ! ← 添加这行 + use registry_module + use config_module + use mesh_module + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Simple Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call registry_init() + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call list_components() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + + if (has_component_simple("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component_simple("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components (simple version) + print *, "5. Testing registry functionality" + print *, "---------------------------------" + print *, "Registry initialized: ", registry_is_initialized() + print *, "Number of components: ", registry_get_size() + print *, "" + + ! Cleanup + call registry_cleanup() + + print *, "=== Simple test completed successfully ===" + +end program test_minimal_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_physics_minimal.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_physics_minimal.f90 new file mode 100644 index 00000000..cf2a28f1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_physics_minimal.f90 @@ -0,0 +1,91 @@ +! tests/test_physics_minimal.f90 +program test_physics_minimal + use precision_module, only: wp, ip + use linear_convection_equation, only: linear_convection_eq, create_linear_convection_eq + use linear_convection_problem, only: linear_convection_prob, create_linear_convection_prob + + implicit none + + type(linear_convection_eq) :: eq + type(linear_convection_prob) :: prob + real(wp) :: u, f, a + real(wp), allocatable :: x(:), u_ic(:), u_exact(:) + integer :: i, nx = 10 + + print *, "=== 最小物理模块测试 ===" + print *, "" + + ! 测试1: 方程功能 + print *, "1. 测试方程功能..." + print *, "-------------------" + + eq = create_linear_convection_eq(wave_speed=2.0_wp) + print *, "方程: ", eq%name + print *, "波速: ", eq%wave_speed + + u = 1.5_wp + f = eq%flux(u) + a = eq%speed() + + print *, "u = ", u + print *, "F(u) = ", f, " (期望: 3.0)" + print *, "波速 a = ", a, " (期望: 2.0)" + + if (abs(f - 3.0_wp) < 1e-10_wp .and. abs(a - 2.0_wp) < 1e-10_wp) then + print *, "✓ 方程功能正常" + else + print *, "✗ 方程功能异常" + end if + print *, "" + + ! 测试2: 问题功能 + print *, "2. 测试问题功能..." + print *, "-------------------" + + prob = create_linear_convection_prob(ic_type="step", domain_length=2.0_wp) + print *, "问题: ", prob%name + print *, "IC类型: ", trim(prob%ic_type) + print *, "域长度: ", prob%domain_length + + allocate(x(nx), u_ic(nx), u_exact(nx)) + do i = 1, nx + x(i) = 0.0_wp + (i-1) * 0.2_wp + end do + + ! 测试初始条件 + call prob%initial_condition(x, u_ic) + print *, "初始条件范围: ", minval(u_ic), " 到 ", maxval(u_ic) + + ! 测试精确解 + u_exact = prob%exact_solution(x, 0.0_wp) + print *, "t=0时精确解范围: ", minval(u_exact), " 到 ", maxval(u_exact) + + ! 检查阶跃函数 + if (abs(u_ic(1) - 1.0_wp) < 1e-10_wp .and. & + abs(u_ic(6) - 2.0_wp) < 1e-10_wp) then + print *, "✓ 阶跃初始条件正确" + else + print *, "✗ 阶跃初始条件错误" + end if + + ! 检查精确解与初始条件一致 + if (maxval(abs(u_ic - u_exact)) < 1e-10_wp) then + print *, "✓ t=0时精确解与初始条件一致" + else + print *, "✗ 精确解计算错误" + end if + print *, "" + + ! 测试3: 边界条件接口 + print *, "3. 测试边界条件接口..." + print *, "----------------------" + call prob%boundary_condition(u_ic, 0.0_wp) + print *, "✓ 边界条件接口正常" + print *, "" + + deallocate(x, u_ic, u_exact) + + print *, "=== 物理模块最小测试完成 ===" + print *, "下一步: 将物理模块集成到现有系统中" + +end program test_physics_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_simple_link.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_simple_link.f90 new file mode 100644 index 00000000..71cc614e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_simple_link.f90 @@ -0,0 +1,78 @@ +! tests/test_simple_link.f90 +program test_simple_link + use base_modules, only: wp ! ← 添加这行 + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call registry_init() + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call list_components() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component_simple("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component_simple("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Cleanup + call registry_cleanup() + + print *, "=== Minimal test completed successfully ===" + +end program test_simple_link \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_solver_framework.f90 b/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_solver_framework.f90 new file mode 100644 index 00000000..6754323d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02g/tests/test_solver_framework.f90 @@ -0,0 +1,91 @@ +! tests/test_solver_framework.f90 +program test_solver_framework + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use mesh_module, only: mesh_type + use solver_module, only: cfd_solver, solver_create, solver_run, solver_cleanup + use solver_module, only: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, & + SOLVER_COMPLETED, SOLVER_ERROR + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(cfd_solver) :: solver + + print *, "=== 求解器框架测试 ===" + print *, "" + + ! 测试1: 基本创建 + print *, "1. 测试求解器创建..." + print *, "----------------------" + + ! 创建配置 + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.0_real64 + config%dt = 0.01_real64 + + call config_print(config) + print *, "" + + ! 创建网格 + call mesh%init(xmin=0.0_real64, xmax=2.0_real64, ncells=20) + call mesh%print_info() + print *, "" + + ! 创建求解器 + solver = solver_create(config, mesh) + print *, "✓ 求解器创建成功" + print *, " 状态: ", solver%get_state() + print *, "" + + ! 测试2: 求解器初始化 + print *, "2. 测试求解器初始化..." + print *, "------------------------" + + call solver%initialize() + print *, "✓ 求解器初始化完成" + print *, " 状态: ", solver%get_state() + print *, " 错误信息: '", trim(solver%get_error()), "'" + print *, "" + + ! 测试3: 简单运行 + print *, "3. 测试求解器运行..." + print *, "----------------------" + + call solver_run(solver, 0.05_real64) ! 运行到0.05秒 + print *, "✓ 求解器运行完成" + print *, " 最终状态: ", solver%get_state() + print *, "" + + ! 测试4: 清理 + print *, "4. 测试求解器清理..." + print *, "----------------------" + + call solver_cleanup(solver) + print *, "✓ 求解器清理完成" + print *, " 状态: ", solver%get_state() + print *, "" + + ! 测试5: 错误处理 + print *, "5. 测试错误处理..." + print *, "-------------------" + + ! 尝试重复初始化 + call solver%initialize() + print *, " 重复初始化状态: ", solver%get_state() + print *, " 错误信息: '", trim(solver%get_error()), "'" + + call solver_cleanup(solver) + print *, "" + + print *, "=== 框架测试总结 ===" + print *, "✓ 求解器创建/初始化/运行/清理流程验证完成" + print *, "✓ 状态管理正常工作" + print *, "✓ 错误处理机制就绪" + print *, "" + print *, "下一步: 添加实际数值计算功能" + +end program test_solver_framework \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02h/CMakeLists.txt new file mode 100644 index 00000000..ef66d584 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/README.md b/example/1d-linear-convection/weno3/fortran/registry/02h/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/02h/scripts/build.bat new file mode 100644 index 00000000..6fd6dc03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/scripts/build.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Project Builder +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python build script with full Intel environment support... +echo. + +python build.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Build failed + pause + exit /b 1 +) + +echo. +echo [INFO] Build completed successfully! +echo. +echo [INFO] To run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/02h/scripts/build.py new file mode 100644 index 00000000..3bf6d537 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/scripts/build.py @@ -0,0 +1,629 @@ +#!/usr/bin/env python3 +""" +Fortran CFD Project Builder - 完整Python解决方案 +在Python内部处理Intel oneAPI环境配置 +""" + +import os +import sys +import subprocess +import shutil +import argparse +import time +import platform +import tempfile +from pathlib import Path + +class IntelEnvironment: + """Intel oneAPI环境管理器""" + + def __init__(self): + self.setvars_path = None + self.env_vars = {} + + def find_setvars(self): + """查找setvars.bat文件""" + possible_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + r"C:\Program Files (x86)\Intel\oneAPI\compiler\latest\env\vars.bat", + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\setvars.bat"), + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\compiler\latest\env\vars.bat"), + ] + + for path in possible_paths: + if os.path.exists(path): + self.setvars_path = path + return True + + return False + + def setup_environment(self): + """设置Intel环境""" + if not self.find_setvars(): + return False + + try: + # 创建临时的批处理文件来捕获环境变量 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + f.write(f'@echo off\n') + f.write(f'call "{self.setvars_path}" >nul 2>&1\n') + f.write(f'set\n') # 输出所有环境变量 + temp_bat = f.name + + # 运行批处理文件并捕获输出 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + shell=True + ) + + # 解析环境变量 + for line in result.stdout.split('\n'): + line = line.strip() + if '=' in line: + key, value = line.split('=', 1) + self.env_vars[key.strip()] = value.strip() + + # 清理临时文件 + os.unlink(temp_bat) + + # 更新当前进程的环境变量 + os.environ.update(self.env_vars) + + return True + + except Exception as e: + print(f"设置Intel环境失败: {e}") + return False + + def get_compiler_info(self): + """获取编译器信息""" + info = {} + + # 检查ifx编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + env={**os.environ, **self.env_vars} if self.env_vars else os.environ + ) + + if result.returncode == 0: + for line in result.stdout.split('\n'): + if 'Version' in line or '版本' in line: + info['ifx_version'] = line.strip() + break + except: + pass + + # 检查环境变量 + info['ifx_root'] = self.env_vars.get('IFX_ROOT', '') + info['compiler_root'] = self.env_vars.get('ONEAPI_ROOT', '') + + return info + +class BuildSystem: + """构建系统主类""" + + def __init__(self): + self.project_root = Path(__file__).parent.parent + self.build_dir = self.project_root / "build" + self.intel_env = IntelEnvironment() + + # 设置控制台编码 + if sys.platform == "win32": + try: + import ctypes + # 设置控制台输出为UTF-8 + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + except: + pass + + def print_header(self, text): + """打印标题""" + print(f"\n{'='*70}") + print(f" {text}") + print(f"{'='*70}\n") + + def print_step(self, step, total, message): + """打印步骤""" + print(f"[{step}/{total}] {message}...") + + def print_success(self, message): + """打印成功""" + print(f"\033[92m✓ {message}\033[0m") + + def print_error(self, message): + """打印错误""" + print(f"\033[91m✗ {message}\033[0m") + + def print_warning(self, message): + """打印警告""" + print(f"\033[93m! {message}\033[0m") + + def print_info(self, message): + """打印信息""" + print(f"\033[94mℹ {message}\033[0m") + + def check_prerequisites(self): + """检查前提条件""" + self.print_step(1, 6, "检查前提条件") + + # 检查Python版本 + python_version = sys.version.split()[0] + self.print_info(f"Python版本: {python_version}") + + # 检查平台 + self.print_info(f"平台: {platform.system()} {platform.release()}") + self.print_info(f"处理器核心数: {os.cpu_count()}") + + # 检查CMake + try: + result = subprocess.run( + ["cmake", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + version_line = result.stdout.split('\n')[0] + self.print_success(f"CMake: {version_line}") + else: + self.print_error("CMake未找到") + return False + except FileNotFoundError: + self.print_error("CMake未安装") + return False + + return True + + def setup_intel_environment(self, args): + """设置Intel环境""" + self.print_step(2, 6, "配置Intel oneAPI环境") + + if not self.intel_env.find_setvars(): + self.print_warning("未找到Intel oneAPI setvars.bat") + self.print_info("将尝试使用系统环境中的编译器") + + # 检查是否能直接访问编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + self.print_success("Intel编译器在系统PATH中找到") + return True + else: + self.print_warning("Intel编译器未在PATH中找到") + except: + self.print_warning("无法访问Intel编译器") + + return True # 继续,让CMake自己找编译器 + + # 设置环境 + if self.intel_env.setup_environment(): + compiler_info = self.intel_env.get_compiler_info() + + if compiler_info.get('ifx_version'): + self.print_success(f"Intel Fortran编译器: {compiler_info['ifx_version']}") + elif compiler_info.get('ifx_root'): + self.print_success(f"Intel编译器路径: {compiler_info['ifx_root']}") + else: + self.print_success("Intel oneAPI环境配置完成") + + return True + else: + self.print_warning("Intel环境配置失败,将继续使用系统环境") + return True + + def clean_build_directory(self, args): + """清理构建目录""" + if args.clean and self.build_dir.exists(): + self.print_info("清理构建目录...") + try: + shutil.rmtree(self.build_dir) + self.print_success("构建目录已清理") + except Exception as e: + self.print_error(f"清理失败: {e}") + if not args.force: + return False + return True + + def run_command(self, cmd, cwd=None, check=True, env=None): + """运行命令""" + if isinstance(cmd, list): + cmd_str = ' '.join(str(c) for c in cmd if c) + else: + cmd_str = str(cmd) + + print(f" \033[96m$\033[0m {cmd_str}") + + try: + # 合并环境变量 + exec_env = os.environ.copy() + if env: + exec_env.update(env) + if self.intel_env.env_vars: + exec_env.update(self.intel_env.env_vars) + + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=False, + env=exec_env + ) + + # 处理输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or '完成' in line or '生成' in line: + print(f" \033[92m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + if check and result.returncode != 0: + self.print_error(f"命令执行失败,退出码: {result.returncode}") + return False + + return True + + except Exception as e: + self.print_error(f"命令执行异常: {e}") + return False + + def configure_cmake(self, args): + """配置CMake""" + self.print_step(3, 6, "配置CMake项目") + + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ] + + if args.compiler == "ifx": + cmake_cmd.extend(["-T", "fortran=ifx"]) + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + success = self.run_command(cmake_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("CMake配置完成") + else: + self.print_error("CMake配置失败") + + return success + + def build_project(self, args): + """构建项目""" + self.print_step(4, 6, "构建项目") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + success = self.run_command(build_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("项目构建完成") + else: + self.print_error("构建失败") + + return success + + def run_tests_with_environment(self, test_exe): + """运行单个测试,确保有Intel环境""" + try: + # 创建临时的批处理文件来运行测试 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'"{test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'"{test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + return result + + except Exception as e: + print(f"运行测试失败: {e}") + return None + + def run_tests(self, args): + """运行测试""" + self.print_step(5, 6, "运行测试") + + # 查找测试可执行文件 + test_dir = self.build_dir / "bin" / args.build_type + if not test_dir.exists(): + test_dir = self.build_dir / "bin" + if not test_dir.exists(): + test_dir = self.build_dir + + test_files = list(test_dir.glob("test_*.exe")) + + if not test_files: + self.print_warning("未找到测试程序") + return True + + all_passed = True + + for test_exe in sorted(test_files): + test_name = test_exe.stem + self.print_info(f"运行测试: {test_name}") + print(f" {'-'*50}") + + # 运行测试 + result = self.run_tests_with_environment(str(test_exe)) + + if result is None: + self.print_error(f" {test_name} 运行失败") + all_passed = False + continue + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + print(f" {line}") + + if result.returncode == 0: + self.print_success(f" {test_name} 通过") + else: + self.print_error(f" {test_name} 失败 (退出码: {result.returncode})") + all_passed = False + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + print() # 空行 + + return all_passed + + def create_test_runner(self, args): + """创建独立的测试运行器""" + self.print_step(6, 6, "创建测试运行器") + + runner_path = self.build_dir / "run_tests.bat" + + content = f'''@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Test Runner +echo ======================================== +echo. + +REM Setup Intel oneAPI environment +set "SETVARS_PATH={self.intel_env.setvars_path or ''}" +if exist "%SETVARS_PATH%" ( + call "%SETVARS_PATH%" >nul + echo [INFO] Intel environment configured +) else ( + echo [WARNING] Intel environment not found + echo [WARNING] Tests may fail without runtime libraries +) + +echo. + +REM Run all test executables +set "TEST_COUNT=0" +set "PASS_COUNT=0" + +for %%f in ("bin\\{args.build_type}\\test_*.exe") do ( + set /a TEST_COUNT+=1 + echo [TEST %%f] + echo {'-'*50} + + %%f + if errorlevel 1 ( + echo [FAILED] %%f + ) else ( + echo [PASSED] %%f + set /a PASS_COUNT+=1 + ) + echo. +) + +echo ======================================== +echo Tests: %PASS_COUNT%/%TEST_COUNT% passed +if %PASS_COUNT% equ %TEST_COUNT% ( + echo [SUCCESS] All tests passed! +) else ( + echo [FAILURE] Some tests failed +) +echo ======================================== + +pause +''' + + with open(runner_path, 'w', encoding='utf-8') as f: + f.write(content) + + self.print_success(f"测试运行器已创建: {runner_path}") + self.print_info(f"使用方法: cd build && run_tests.bat") + + return runner_path + + def generate_report(self, args, build_time, tests_passed): + """生成构建报告""" + self.print_header("构建完成") + + print(f"项目: {self.project_root.name}") + print(f"构建类型: {args.build_type}") + print(f"编译器: {args.compiler}") + print(f"并行作业: {args.jobs}") + print(f"总耗时: {build_time:.1f}秒") + print(f"测试结果: {'全部通过' if tests_passed else '有失败'}") + + # 显示生成的可执行文件 + bin_dir = self.build_dir / "bin" / args.build_type + if bin_dir.exists(): + print(f"\n生成的可执行文件:") + for exe in sorted(bin_dir.glob("*.exe")): + size_mb = exe.stat().st_size / (1024 * 1024) + print(f" • {exe.name} ({size_mb:.2f} MB)") + + # 显示测试运行器信息 + runner_path = self.build_dir / "run_tests.bat" + if runner_path.exists(): + print(f"\n独立测试运行器:") + print(f" • {runner_path.name}") + print(f" 在Intel oneAPI环境中运行所有测试") + + def run(self): + """运行构建系统""" + parser = argparse.ArgumentParser( + description="Fortran CFD项目构建工具", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认构建 + %(prog)s --clean # 清理后构建 + %(prog)s --build-type Release # Release构建 + %(prog)s --no-tests # 只构建,不运行测试 + %(prog)s -j8 --verbose # 8线程并行构建,详细输出 + """ + ) + + parser.add_argument("--build-type", choices=["Debug", "Release"], + default="Debug", help="构建类型") + parser.add_argument("--compiler", choices=["ifx", "ifort"], + default="ifx", help="Fortran编译器") + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-tests", action="store_true", + help="跳过测试") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + + args = parser.parse_args() + + # 开始构建 + start_time = time.time() + + self.print_header("Fortran CFD 项目构建系统") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 检查前提条件 + if not self.check_prerequisites(): + if not args.force: + return 1 + + # 2. 设置Intel环境 + if not self.setup_intel_environment(args): + if not args.force: + return 1 + + # 3. 清理目录 + if not self.clean_build_directory(args): + if not args.force: + return 1 + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 4. 配置CMake + if not self.configure_cmake(args): + if not args.force: + return 1 + + # 5. 构建项目 + if not self.build_project(args): + if not args.force: + return 1 + + # 6. 运行测试和创建测试运行器 + tests_passed = True + if not args.no_tests: + tests_passed = self.run_tests(args) + + # 创建测试运行器 + self.create_test_runner(args) # 传递 args 参数 + + # 7. 生成报告 + build_time = time.time() - start_time + self.generate_report(args, build_time, tests_passed) + + return 0 if tests_passed else 1 + + except KeyboardInterrupt: + self.print_error("\n构建被用户中断") + return 1 + except Exception as e: + self.print_error(f"构建过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + builder = BuildSystem() + return builder.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/02h/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/scripts/run_all_steps.bat b/example/1d-linear-convection/weno3/fortran/registry/02h/scripts/run_all_steps.bat new file mode 100644 index 00000000..d506149b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/scripts/run_all_steps.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo CFD Project: All Steps +echo ======================================== +echo. + +echo [INFO] Starting Step 1: Physics Modules Test... +call run_step1.bat + +if errorlevel 1 ( + echo [ERROR] Step 1 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Starting Step 2: Configuration Physics Update... +call run_step2.bat + +if errorlevel 1 ( + echo [ERROR] Step 2 failed + pause + exit /b 1 +) + +echo. +echo ======================================== +echo All Steps Completed Successfully! +echo ======================================== +echo. +echo [INFO] Next: Update component manager for physics support +echo [INFO] Run: run_step3.bat (to be created) +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/scripts/run_step1.bat b/example/1d-linear-convection/weno3/fortran/registry/02h/scripts/run_step1.bat new file mode 100644 index 00000000..0b6b1f17 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/scripts/run_step1.bat @@ -0,0 +1,39 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Step 1: Physics Modules Test +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python step1 script with full Intel environment support... +echo. + +python run_step1.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Step 1 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Step 1 completed successfully! +echo. +echo [INFO] Next step: Update config to include physics settings +echo [INFO] Run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/scripts/run_step1.py b/example/1d-linear-convection/weno3/fortran/registry/02h/scripts/run_step1.py new file mode 100644 index 00000000..5e087a69 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/scripts/run_step1.py @@ -0,0 +1,319 @@ +#!/usr/bin/env python3 +""" +Step 1: Physics Modules Test +扩展build.py,专门用于测试物理模块 +""" + +import os +import sys +import subprocess +import time +from pathlib import Path + +# 添加当前目录到路径,以便导入build.py的类 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import IntelEnvironment, BuildSystem +except ImportError: + print("Error: Cannot import build.py. Make sure build.py is in the same directory.") + sys.exit(1) + +class Step1System(BuildSystem): + """Step 1 测试系统,继承自BuildSystem""" + + def __init__(self): + super().__init__() + self.test_name = "test_physics_minimal" + self.test_exe = None + + def find_test_executable(self): + """查找测试可执行文件""" + possible_paths = [ + self.build_dir / "bin" / "Debug" / f"{self.test_name}.exe", + self.build_dir / "Debug" / f"{self.test_name}.exe", + self.build_dir / f"{self.test_name}.exe", + self.build_dir / "bin" / f"{self.test_name}.exe", + ] + + for path in possible_paths: + if path.exists(): + self.test_exe = path + self.print_success(f"Found test executable: {path}") + return True + + # 如果没有找到,尝试搜索 + self.print_warning(f"Could not find {self.test_name}.exe") + self.print_info("Searching for test executables...") + + try: + result = subprocess.run( + ["dir", str(self.build_dir), "/s", "/b", "*.exe"], + capture_output=True, + text=True, + encoding='utf-8', + shell=True + ) + + if result.returncode == 0: + test_files = [line.strip() for line in result.stdout.split('\n') + if line and 'test_' in line.lower()] + + if test_files: + self.print_info("Found test files:") + for test_file in test_files: + self.print_info(f" {test_file}") + return False + except: + pass + + return False + + def run_test_with_intel_env(self): + """在Intel环境下运行测试""" + if not self.test_exe: + self.print_error("No test executable found") + return False + + self.print_step(1, 2, f"Running test: {self.test_exe.name}") + + try: + # 创建临时的批处理文件来运行测试(包含Intel环境) + import tempfile + + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + # 设置Intel环境 + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'echo [INFO] Intel environment configured\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'echo [WARNING] Intel environment not found\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + self.print_info(f"Command: {temp_bat}") + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower() or 'fail' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or 'pass' in line.lower() or '✓' in line: + print(f" \033[92m{line}\033[0m") + elif '=' in line or '---' in line: + print(f" \033[96m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + self.print_warning("Test stderr output:") + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + self.print_step(2, 2, "Test execution completed") + + if result.returncode == 0: + self.print_success("Test passed") + return True + else: + self.print_error(f"Test failed (exit code: {result.returncode})") + return False + + except Exception as e: + self.print_error(f"Failed to run test: {e}") + return False + + def build_project_if_needed(self, args): + """如果需要,构建项目""" + if args.no_build: + self.print_info("Skipping build (--no-build flag)") + return True + + self.print_step(1, 3, "Building project") + + # 调用父类的构建方法 + build_args = argparse.Namespace() + build_args.clean = args.clean + build_args.build_type = "Debug" + build_args.compiler = "ifx" + build_args.no_tests = True # 不运行所有测试 + build_args.jobs = os.cpu_count() + build_args.verbose = args.verbose + build_args.force = args.force + + # 清理构建目录 + if args.clean and self.build_dir.exists(): + self.print_info("Cleaning build directory...") + import shutil + try: + shutil.rmtree(self.build_dir) + self.print_success("Build directory cleaned") + except Exception as e: + self.print_error(f"Clean failed: {e}") + if not args.force: + return False + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 配置CMake + self.print_step(2, 3, "Configuring CMake") + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-DCMAKE_BUILD_TYPE=Debug", + "-T", "fortran=ifx", + ] + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + if not self.run_command(cmake_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + # 构建项目 + self.print_step(3, 3, "Building project") + build_cmd = [ + "cmake", + "--build", ".", + "--config", "Debug", + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + if not self.run_command(build_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + self.print_success("Build completed") + return True + + def run(self): + """运行Step 1测试""" + parser = argparse.ArgumentParser( + description="Step 1: Physics Modules Test", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认运行 + %(prog)s --clean # 清理后构建并测试 + %(prog)s --no-build # 只运行测试,不重新构建 + %(prog)s --verbose # 详细输出 + %(prog)s -j4 # 使用4个并行作业构建 + """ + ) + + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-build", action="store_true", + help="不重新构建,直接运行测试") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + + args = parser.parse_args() + + # 开始测试 + start_time = time.time() + + self.print_header("Step 1: Physics Modules Implementation Test") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 设置Intel环境 + self.print_step(1, 4, "Setting up Intel oneAPI environment") + if not self.setup_intel_environment(args): + if not args.force: + self.print_error("Intel environment setup failed") + return 1 + self.print_warning("Intel environment setup failed, continuing...") + + # 2. 构建项目(如果需要) + self.print_step(2, 4, "Building project if needed") + if not self.build_project_if_needed(args): + if not args.force: + return 1 + + # 3. 查找测试可执行文件 + self.print_step(3, 4, "Finding test executable") + if not self.find_test_executable(): + if not args.force: + return 1 + self.print_warning("Test executable not found, but continuing due to --force") + return 0 + + # 4. 运行测试 + self.print_step(4, 4, "Running physics module test") + test_passed = self.run_test_with_intel_env() + + # 生成报告 + test_time = time.time() - start_time + self.print_header("Step 1 Complete") + + print(f"测试: {'通过 ✓' if test_passed else '失败 ✗'}") + print(f"测试程序: {self.test_exe.name if self.test_exe else '未找到'}") + print(f"总耗时: {test_time:.1f}秒") + + if test_passed: + print(f"\n下一步: 更新配置以包含物理设置") + print(f"建议: 修改config.f90,添加physics相关字段") + return 0 + else: + if args.force: + self.print_warning("测试失败,但由于--force标志继续执行") + return 0 + return 1 + + except KeyboardInterrupt: + self.print_error("\n测试被用户中断") + return 1 + except Exception as e: + self.print_error(f"测试过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + system = Step1System() + return system.run() + +if __name__ == "__main__": + # 需要导入argparse + import argparse + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/scripts/run_step2.bat b/example/1d-linear-convection/weno3/fortran/registry/02h/scripts/run_step2.bat new file mode 100644 index 00000000..9c1f62de --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/scripts/run_step2.bat @@ -0,0 +1,39 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Step 2: Configuration Physics Update +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python step2 script with full Intel environment support... +echo. + +python run_step2.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Step 2 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Step 2 completed successfully! +echo. +echo [INFO] Next step: Update component manager to support physics +echo [INFO] Run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/scripts/run_step2.py b/example/1d-linear-convection/weno3/fortran/registry/02h/scripts/run_step2.py new file mode 100644 index 00000000..c16b7608 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/scripts/run_step2.py @@ -0,0 +1,284 @@ +#!/usr/bin/env python3 +""" +Step 2: Configuration Physics Update +测试配置模块的物理功能更新 +""" + +import os +import sys +import subprocess +import time +from pathlib import Path + +# 添加当前目录到路径,以便导入build.py的类 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import IntelEnvironment, BuildSystem +except ImportError: + print("Error: Cannot import build.py. Make sure build.py is in the same directory.") + sys.exit(1) + +class Step2System(BuildSystem): + """Step 2 测试系统,继承自BuildSystem""" + + def __init__(self): + super().__init__() + self.test_name = "test_config_physics" + self.test_exe = None + + def find_test_executable(self): + """查找测试可执行文件""" + possible_paths = [ + self.build_dir / "bin" / "Debug" / f"{self.test_name}.exe", + self.build_dir / "Debug" / f"{self.test_name}.exe", + self.build_dir / f"{self.test_name}.exe", + self.build_dir / "bin" / f"{self.test_name}.exe", + ] + + for path in possible_paths: + if path.exists(): + self.test_exe = path + self.print_success(f"Found test executable: {path}") + return True + + return False + + def run_test_with_intel_env(self): + """在Intel环境下运行测试""" + if not self.test_exe: + self.print_error("No test executable found") + return False + + self.print_step(1, 2, f"Running test: {self.test_exe.name}") + + try: + # 创建临时的批处理文件来运行测试(包含Intel环境) + import tempfile + + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + # 设置Intel环境 + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'echo [INFO] Intel environment configured\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'echo [WARNING] Intel environment not found\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + self.print_info(f"Command: {temp_bat}") + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower() or 'fail' in line.lower() or '✗' in line: + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or 'pass' in line.lower() or '✓' in line: + print(f" \033[92m{line}\033[0m") + elif '=' in line or '---' in line or '===' in line: + print(f" \033[96m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + self.print_warning("Test stderr output:") + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + self.print_step(2, 2, "Test execution completed") + + if result.returncode == 0: + self.print_success("Test passed") + return True + else: + self.print_error(f"Test failed (exit code: {result.returncode})") + return False + + except Exception as e: + self.print_error(f"Failed to run test: {e}") + return False + + def build_project(self, args): + """构建项目""" + if args.no_build: + self.print_info("Skipping build (--no-build flag)") + return True + + self.print_step(1, 3, "Building project") + + # 清理构建目录 + if args.clean and self.build_dir.exists(): + self.print_info("Cleaning build directory...") + import shutil + try: + shutil.rmtree(self.build_dir) + self.print_success("Build directory cleaned") + except Exception as e: + self.print_error(f"Clean failed: {e}") + if not args.force: + return False + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 配置CMake + self.print_step(2, 3, "Configuring CMake") + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-DCMAKE_BUILD_TYPE=Debug", + "-T", "fortran=ifx", + ] + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + if not self.run_command(cmake_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + # 构建项目 + self.print_step(3, 3, "Building project") + build_cmd = [ + "cmake", + "--build", ".", + "--config", "Debug", + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + if not self.run_command(build_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + self.print_success("Build completed") + return True + + def run(self): + """运行Step 2测试""" + import argparse + + parser = argparse.ArgumentParser( + description="Step 2: Configuration Physics Update", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认运行 + %(prog)s --clean # 清理后构建并测试 + %(prog)s --no-build # 只运行测试,不重新构建 + %(prog)s --verbose # 详细输出 + """ + ) + + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-build", action="store_true", + help="不重新构建,直接运行测试") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + + args = parser.parse_args() + + # 开始测试 + start_time = time.time() + + self.print_header("Step 2: Configuration Physics Update") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 设置Intel环境 + self.print_step(1, 4, "Setting up Intel oneAPI environment") + if not self.setup_intel_environment(args): + if not args.force: + self.print_error("Intel environment setup failed") + return 1 + self.print_warning("Intel environment setup failed, continuing...") + + # 2. 构建项目(如果需要) + self.print_step(2, 4, "Building project if needed") + if not self.build_project(args): + if not args.force: + return 1 + + # 3. 查找测试可执行文件 + self.print_step(3, 4, "Finding test executable") + if not self.find_test_executable(): + self.print_error(f"Test executable {self.test_name}.exe not found") + if not args.force: + return 1 + self.print_warning("Test executable not found, but continuing due to --force") + return 0 + + # 4. 运行测试 + self.print_step(4, 4, "Running configuration physics test") + test_passed = self.run_test_with_intel_env() + + # 生成报告 + test_time = time.time() - start_time + self.print_header("Step 2 Complete") + + print(f"测试: {'通过 ✓' if test_passed else '失败 ✗'}") + print(f"测试程序: {self.test_exe.name if self.test_exe else '未找到'}") + print(f"总耗时: {test_time:.1f}秒") + + if test_passed: + print(f"\n下一步: 更新组件管理器以支持物理模块") + print(f"建议: 修改component_manager.f90,添加physics组件创建") + return 0 + else: + if args.force: + self.print_warning("测试失败,但由于--flag继续执行") + return 0 + return 1 + + except KeyboardInterrupt: + self.print_error("\n测试被用户中断") + return 1 + except Exception as e: + self.print_error(f"测试过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + system = Step2System() + return system.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02h/src/CMakeLists.txt new file mode 100644 index 00000000..8a76bdb5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/CMakeLists.txt @@ -0,0 +1,17 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(base) +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) +add_subdirectory(physics) # ← 新增物理模块目录 +add_subdirectory(manager) + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/base/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02h/src/base/CMakeLists.txt new file mode 100644 index 00000000..74f4aa65 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/base/CMakeLists.txt @@ -0,0 +1,16 @@ +# src/base/CMakeLists.txt +message(STATUS "Configuring base module...") + +add_library(base STATIC + modules.f90 + precision.f90 # 新增 +) + +set_target_properties(base PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Base module configured") + +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/base/modules.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/src/base/modules.f90 new file mode 100644 index 00000000..43aaee24 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/base/modules.f90 @@ -0,0 +1,36 @@ +! src/base/modules.f90 +module base_modules + use, intrinsic :: iso_fortran_env, only: real64, int32 + implicit none + + public :: wp, ip, max_name_len, string_len, cfd_config_base, component_info + + integer, parameter :: wp = real64 + integer, parameter :: ip = int32 + integer, parameter :: string_len = 100 + integer, parameter :: max_name_len = 32 + + ! 基础配置类型 + type :: cfd_config_base + character(len=max_name_len) :: ic_type = "step" + character(len=max_name_len) :: recon_scheme = "eno" + character(len=max_name_len) :: flux_type = "rusanov" + integer(ip) :: rk_order = 1 + real(wp) :: wave_speed = 1.0_wp + real(wp) :: final_time = 0.625_wp + real(wp) :: dt = 0.025_wp + character(len=max_name_len) :: boundary_type = "periodic" + integer(ip) :: spatial_order = 2 + character(len=max_name_len) :: equation_type = "linear_advection" + character(len=max_name_len) :: problem_type = "linear_advection" + logical :: verbose = .true. + end type cfd_config_base + + ! 组件信息类型 + type :: component_info + character(len=max_name_len) :: category = "" + character(len=max_name_len) :: name = "" + integer(ip) :: order = 0 + end type component_info + +end module base_modules \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/base/precision.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/src/base/precision.f90 new file mode 100644 index 00000000..4ac5fd7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/base/precision.f90 @@ -0,0 +1,9 @@ +! src/base/precision.f90(简单版本) +module precision_module + use base_modules, only: wp, ip + implicit none + + ! 重新导出,确保兼容 + public :: wp, ip + +end module precision_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02h/src/core/CMakeLists.txt new file mode 100644 index 00000000..d8b8df06 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/core/CMakeLists.txt @@ -0,0 +1,14 @@ +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") + +add_library(core STATIC + registry.f90 +) + +target_link_libraries(core PRIVATE base) + +set_target_properties(core PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Core module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/core/factory_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/src/core/factory_base.f90 new file mode 100644 index 00000000..302418a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/core/factory_base.f90 @@ -0,0 +1,57 @@ +! src/core/factory_base.f90 +module factory_base_module + use base_modules, only: wp, ip + use registry_module, only: create_component, has_component + + implicit none + private + public :: wp, ip, factory_base, factory_create + + ! 工厂基类 + type :: factory_base + character(len=max_name_length) :: category = "" + contains + procedure :: create => factory_base_create + procedure :: get_available => factory_base_get_available + end type factory_base + + ! 便捷函数类型 + abstract interface + function factory_function_interface(category, name) result(instance) + import :: wp + character(len=*), intent(in) :: category, name + class(*), allocatable :: instance + end function factory_function_interface + end interface + +contains + + ! 创建工厂实例 + function factory_create(category) result(factory) + character(len=*), intent(in) :: category + type(factory_base) :: factory + factory%category = trim(category) + end function factory_create + + ! 工厂创建方法 + function factory_base_create(this, name) result(instance) + class(factory_base), intent(in) :: this + character(len=*), intent(in) :: name + class(*), allocatable :: instance + + instance = create_component(this%category, name) + end function factory_base_create + + ! 获取可用组件列表(简化版) + subroutine factory_base_get_available(this, names, count) + class(factory_base), intent(in) :: this + character(len=*), allocatable, intent(out) :: names(:) + integer(ip), intent(out) :: count + + ! 这里需要实现从注册表获取列表的逻辑 + ! 暂时返回空列表 + count = 0 + allocate(character(len=max_name_length) :: names(0)) + end subroutine factory_base_get_available + +end module factory_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..a960167c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/src/core/registry.f90 new file mode 100644 index 00000000..acc63edb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/core/registry.f90 @@ -0,0 +1,203 @@ +! src/core/registry.f90 +module registry_module + use base_modules, only: wp, ip, max_name_len, component_info + + implicit none + private + + ! 明确公开所有需要的接口 + public :: wp, ip ! 类型参数 + public :: component_info ! 类型 + public :: registry_init, registry_cleanup ! 初始化/清理 + public :: register_component_simple ! 注册组件 + public :: has_component_simple ! 检查组件 + public :: list_components ! 列出组件 + public :: registry_is_initialized ! 检查初始化状态 ← 新增 + public :: registry_get_size ! 获取大小 ← 新增 + + ! 全局注册表 + type :: component_registry + type(component_info), allocatable :: components(:) + integer(ip) :: count = 0 + integer(ip) :: capacity = 100 + logical :: initialized = .false. + logical :: verbose = .true. + end type component_registry + + type(component_registry) :: registry + +contains + + ! ==================== 公共API ==================== + + subroutine registry_init(verbose) + logical, optional, intent(in) :: verbose + + if (registry%initialized) then + if (registry%verbose) then + print *, "[REGISTRY] Already initialized" + end if + return + end if + + if (present(verbose)) then + registry%verbose = verbose + end if + + allocate(registry%components(registry%capacity)) + registry%initialized = .true. + + if (registry%verbose) then + print *, "[REGISTRY] Initialized with capacity:", registry%capacity + end if + end subroutine registry_init + + subroutine registry_cleanup() + if (allocated(registry%components)) then + deallocate(registry%components) + end if + registry%initialized = .false. + registry%count = 0 + + if (registry%verbose) then + print *, "[REGISTRY] Cleaned up" + end if + end subroutine registry_cleanup + + subroutine register_component_simple(category, name, order) + character(len=*), intent(in) :: category, name + integer(ip), optional, intent(in) :: order + + integer(ip) :: i + type(component_info) :: info + + if (.not. registry%initialized) then + call registry_init() + end if + + ! 检查是否已存在 + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + if (registry%verbose) then + print *, "[WARN] Overwriting component: ", trim(category), ".", trim(name) + end if + + ! 更新 + if (present(order)) then + registry%components(i)%order = order + else + registry%components(i)%order = 0 + end if + return + end if + end do + + ! 扩展数组 + if (registry%count >= registry%capacity) then + call expand_registry() + end if + + ! 添加新组件 + registry%count = registry%count + 1 + + info%category = trim(category) + info%name = trim(name) + info%order = 0 + if (present(order)) then + info%order = order + end if + + registry%components(registry%count) = info + + if (registry%verbose) then + print *, "[OK] Registered simple: ", trim(category), ".", trim(name) + end if + end subroutine register_component_simple + + logical function has_component_simple(category, name) + character(len=*), intent(in) :: category, name + + integer(ip) :: i + + has_component_simple = .false. + + if (.not. registry%initialized) return + + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + has_component_simple = .true. + return + end if + end do + end function has_component_simple + + subroutine list_components(category) + character(len=*), optional, intent(in) :: category + + integer(ip) :: i, count + + if (.not. registry%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + if (registry%count == 0) then + print *, "[INFO] No components registered" + return + end if + + count = 0 + print *, "=== Registry Contents ===" + do i = 1, registry%count + if (.not. present(category) .or. & + trim(registry%components(i)%category) == trim(category)) then + call print_component_info(registry%components(i)) + count = count + 1 + end if + end do + + print *, "Total:", count, "components" + print *, "==========================" + end subroutine list_components + + ! ==================== 新增函数 ==================== + + logical function registry_is_initialized() + ! 检查注册表是否已初始化 + registry_is_initialized = registry%initialized + end function registry_is_initialized + + integer(ip) function registry_get_size() + ! 获取注册表中的组件数量 + registry_get_size = registry%count + end function registry_get_size + + ! ==================== 内部辅助函数 ==================== + + subroutine expand_registry() + type(component_info), allocatable :: temp(:) + + registry%capacity = registry%capacity * 2 + allocate(temp(registry%capacity)) + temp(1:registry%count) = registry%components(1:registry%count) + call move_alloc(temp, registry%components) + + if (registry%verbose) then + print *, "[INFO] Registry expanded to capacity:", registry%capacity + end if + end subroutine expand_registry + + subroutine print_component_info(info) + type(component_info), intent(in) :: info + + if (info%order > 0) then + print *, " [", trim(info%category), ".", trim(info%name), & + " (order:", info%order, ")]" + else + print *, " [", trim(info%category), ".", trim(info%name), "]" + end if + end subroutine print_component_info + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02h/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..70cbbd2f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/infrastructure/CMakeLists.txt @@ -0,0 +1,17 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "Configuring infrastructure module...") + +add_library(infrastructure STATIC + config.f90 + mesh.f90 + domain.f90 # 新增 + solution.f90 # 新增 +) + +target_link_libraries(infrastructure PRIVATE base) + +set_target_properties(infrastructure PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Infrastructure module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/src/infrastructure/config.f90 new file mode 100644 index 00000000..7586a1a5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/infrastructure/config.f90 @@ -0,0 +1,144 @@ +! src/infrastructure/config.f90 (修复版) +module config_module + use base_modules, only: wp, ip, max_name_len, cfd_config_base + + implicit none + public :: wp, ip, cfd_config, config_print, config_with_reconstruction + + ! 扩展配置类型 - 添加物理相关字段 + type, extends(cfd_config_base) :: cfd_config + ! 物理参数 + real(wp) :: left_boundary_value = 1.0_wp + real(wp) :: right_boundary_value = 2.0_wp + real(wp) :: domain_length = 2.0_wp + + ! 新增:物理模块相关配置 + real(wp) :: pulse_center = 0.5_wp ! 高斯脉冲中心 + real(wp) :: pulse_width = 0.1_wp ! 高斯脉冲宽度 + logical :: enable_physics = .true. ! 是否启用物理模块 + contains + ! 新增:物理相关配置方法 + procedure :: set_physics_parameters + procedure :: get_physics_info + end type cfd_config + +contains + + subroutine config_print(cfg) + type(cfd_config), intent(in) :: cfg + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(cfg%ic_type) + print *, "Reconstruction: ", trim(cfg%recon_scheme), " (order:", cfg%spatial_order, ")" + print *, "Flux type: ", trim(cfg%flux_type) + print *, "Time integration: RK", cfg%rk_order + print *, "Wave speed: ", cfg%wave_speed + print *, "Final time: ", cfg%final_time + print *, "Time step: ", cfg%dt + print *, "Boundary: ", trim(cfg%boundary_type) + + ! 新增:物理配置信息 + print *, "--- Physics Configuration ---" + print *, "Equation type: ", trim(cfg%equation_type) + print *, "Problem type: ", trim(cfg%problem_type) + print *, "Domain length: ", cfg%domain_length + print *, "Physics enabled: ", cfg%enable_physics + + if (cfg%ic_type == "gaussian") then + print *, "Pulse center: ", cfg%pulse_center + print *, "Pulse width: ", cfg%pulse_width + end if + + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(cfg, scheme, order) + type(cfd_config), intent(inout) :: cfg + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + integer :: i + + ! 转换为小写 + cfg%recon_scheme = scheme + do i = 1, len_trim(cfg%recon_scheme) + if (cfg%recon_scheme(i:i) >= 'A' .and. cfg%recon_scheme(i:i) <= 'Z') then + cfg%recon_scheme(i:i) = char(ichar(cfg%recon_scheme(i:i)) + 32) + end if + end do + + ! 设置阶数 + if (present(order)) then + cfg%spatial_order = order + else + if (index(cfg%recon_scheme, 'weno') > 0) then + cfg%spatial_order = 5 + else if (trim(cfg%recon_scheme) == 'eno') then + cfg%spatial_order = 3 + end if + end if + + if (cfg%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(cfg%recon_scheme), & + " Order: ", cfg%spatial_order + end if + end subroutine config_with_reconstruction + + ! ========== 新增:物理参数设置方法 ========== + + subroutine set_physics_parameters(this, equation_type, problem_type, & + domain_length, enable_physics) + class(cfd_config), intent(inout) :: this + character(len=*), intent(in), optional :: equation_type, problem_type + real(wp), intent(in), optional :: domain_length + logical, intent(in), optional :: enable_physics + + if (present(equation_type)) then + this%equation_type = trim(equation_type) + if (this%verbose) then + print *, "[CONFIG] Set equation type: ", trim(this%equation_type) + end if + end if + + if (present(problem_type)) then + this%problem_type = trim(problem_type) + if (this%verbose) then + print *, "[CONFIG] Set problem type: ", trim(this%problem_type) + end if + end if + + if (present(domain_length)) then + this%domain_length = domain_length + if (this%verbose) then + print *, "[CONFIG] Set domain length: ", this%domain_length + end if + end if + + if (present(enable_physics)) then + this%enable_physics = enable_physics + if (this%verbose) then + print *, "[CONFIG] Physics module enabled: ", this%enable_physics + end if + end if + end subroutine set_physics_parameters + + subroutine get_physics_info(this) + class(cfd_config), intent(in) :: this + + print *, "=== Physics Configuration Info ===" + print *, "Equation type: ", trim(this%equation_type) + print *, "Problem type: ", trim(this%problem_type) + print *, "Domain length: ", this%domain_length + print *, "Wave speed: ", this%wave_speed + print *, "Physics enabled: ", this%enable_physics + + if (this%ic_type == "gaussian") then + print *, "Pulse parameters:" + print *, " Center: ", this%pulse_center + print *, " Width: ", this%pulse_width + end if + + print *, "==================================" + end subroutine get_physics_info + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/infrastructure/domain.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/src/infrastructure/domain.f90 new file mode 100644 index 00000000..c3662f03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/infrastructure/domain.f90 @@ -0,0 +1,102 @@ +! src/infrastructure/domain.f90 +module domain_module + use base_modules, only: wp, ip, max_name_len + use config_module, only: cfd_config + use mesh_module, only: mesh_type + + implicit none + private + public :: wp, ip, domain_type, domain_create, is_physical_cell + + type :: domain_type + type(cfd_config), pointer :: config => null() + type(mesh_type), pointer :: mesh => null() + integer(ip) :: nghosts = 0 + integer(ip) :: ist = 1 ! 物理区域起始索引(1-based) + integer(ip) :: ied = 1 ! 物理区域结束索引(exclusive) + integer(ip) :: ntcells = 0 ! 总单元数(含ghost) + contains + procedure :: print_info => domain_print_info + procedure :: get_physical_indices => domain_get_physical_indices + end type domain_type + +contains + + function domain_create(config, mesh) result(domain) + type(cfd_config), target, intent(in) :: config + type(mesh_type), target, intent(in) :: mesh + type(domain_type) :: domain + + domain%config => config + domain%mesh => mesh + + ! 计算ghost层数(参考Julia的_calc_nghosts) + domain%nghosts = calc_nghosts(config) + domain%ist = domain%nghosts + 1 + domain%ied = domain%ist + mesh%ncells + domain%ntcells = mesh%ncells + 2 * domain%nghosts + + if (config%verbose) then + print *, "[DOMAIN] Created:" + print *, " Ghost layers: ", domain%nghosts + print *, " Physical cells: ", domain%ist, " to ", domain%ied - 1 + print *, " Total cells: ", domain%ntcells + end if + end function domain_create + + function calc_nghosts(config) result(nghosts) + type(cfd_config), intent(in) :: config + integer(ip) :: nghosts + + character(len=max_name_len) :: scheme + + scheme = config%recon_scheme + + if (scheme == "eno") then + nghosts = config%spatial_order + else if (index(scheme, "weno") > 0) then + nghosts = config%spatial_order / 2 + 1 + else + print *, "[WARNING] Unknown scheme, using default nghosts=2" + nghosts = 2 + end if + + if (nghosts <= 0) then + print *, "[ERROR] Invalid nghosts: ", nghosts + nghosts = 2 + end if + end function calc_nghosts + + logical function is_physical_cell(this, idx) + class(domain_type), intent(in) :: this + integer(ip), intent(in) :: idx + is_physical_cell = (idx >= this%ist .and. idx < this%ied) + end function is_physical_cell + + function domain_get_physical_indices(this) result(indices) + class(domain_type), intent(in) :: this + integer(ip), allocatable :: indices(:) + integer(ip) :: i, count + + count = this%ied - this%ist + allocate(indices(count)) + + do i = 1, count + indices(i) = this%ist + i - 1 + end do + end function domain_get_physical_indices + + subroutine domain_print_info(this) + class(domain_type), intent(in) :: this + + print *, "=== Domain Information ===" + print *, "Configuration: ", trim(this%config%recon_scheme), & + " order ", this%config%spatial_order + print *, "Ghost layers: ", this%nghosts + print *, "Physical cells: ", this%ist, " to ", this%ied - 1 + print *, "Total cells: ", this%ntcells + print *, "Mesh cells: ", this%mesh%ncells + print *, "==========================" + end subroutine domain_print_info + +end module domain_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..f810f3a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/infrastructure/mesh.f90 @@ -0,0 +1,73 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use base_modules, only: wp, ip + + implicit none + public :: wp, ip, mesh_type, mesh_init, mesh_print_info + + ! 网格类型 + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer(ip) :: ncells = 40 + integer(ip) :: nnodes + integer(ip) :: nx + real(wp), allocatable :: x(:) ! 节点坐标 + real(wp), allocatable :: xcc(:) ! 单元中心坐标 + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer(ip), optional, intent(in) :: ncells + + integer(ip) :: i + + ! 设置参数 + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! 计算 + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! 分配内存 + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! 生成节点坐标 + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! 生成单元中心坐标 + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== Mesh Information ===" + print *, "Domain: [", this%xmin, ", ", this%xmax, "]" + print *, "Cells: ", this%ncells + print *, "Nodes: ", this%nnodes + print *, "dx: ", this%dx + print *, "L: ", this%L + print *, "========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/infrastructure/solution.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/src/infrastructure/solution.f90 new file mode 100644 index 00000000..ce88fd8a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/infrastructure/solution.f90 @@ -0,0 +1,131 @@ +! src/infrastructure/solution.f90 +module solution_module + use base_modules, only: wp, ip + use domain_module, only: domain_type + + implicit none + private + public :: wp, ip, solution_type, solution_create, solution_reset + + type :: solution_type + type(domain_type), pointer :: domain => null() + real(wp), allocatable :: u(:) ! 当前解(含ghost) + real(wp), allocatable :: un(:) ! 旧解 + real(wp), allocatable :: q_face_left(:) ! 左界面值 + real(wp), allocatable :: q_face_right(:)! 右界面值 + real(wp), allocatable :: flux(:) ! 通量 + real(wp), allocatable :: res(:) ! 残差 + contains + procedure :: initialize => solution_initialize + procedure :: update_old_field => solution_update_old_field + procedure :: print_info => solution_print_info + procedure :: reset => solution_reset_instance + end type solution_type + +contains + + function solution_create(domain) result(solution) + type(domain_type), target, intent(in) :: domain + type(solution_type) :: solution + + integer(ip) :: ncells, nnodes, ntcells + + solution%domain => domain + + ncells = domain%mesh%ncells + nnodes = domain%mesh%nnodes + ntcells = domain%ntcells + + ! 分配数组(与Julia solution.jl一致) + allocate(solution%u(ntcells), source=0.0_wp) + allocate(solution%un(ntcells), source=0.0_wp) + allocate(solution%q_face_left(nnodes), source=0.0_wp) + allocate(solution%q_face_right(nnodes), source=0.0_wp) + allocate(solution%flux(nnodes), source=0.0_wp) + allocate(solution%res(ncells), source=0.0_wp) + + if (domain%config%verbose) then + print *, "[SOLUTION] Created:" + print *, " u size: ", size(solution%u), " (with ghosts)" + print *, " flux size: ", size(solution%flux) + print *, " res size: ", size(solution%res) + end if + end function solution_create + + subroutine solution_initialize(this, initial_values) + class(solution_type), intent(inout) :: this + real(wp), intent(in), optional :: initial_values(:) + + integer(ip) :: i, idx + type(domain_type), pointer :: domain + + domain => this%domain + + if (present(initial_values)) then + ! 应用初始值到物理区域 + do i = domain%ist, domain%ied - 1 + idx = i - domain%ist + 1 + if (idx <= size(initial_values)) then + this%u(i) = initial_values(idx) + end if + end do + else + ! 默认为0 + this%u = 0.0_wp + end if + + ! 同步旧场(与Julia的update_old_field一致) + call this%update_old_field() + + if (domain%config%verbose) then + print *, "[SOLUTION] Initialized" + print *, " u range: ", minval(this%u), " to ", maxval(this%u) + end if + end subroutine solution_initialize + + subroutine solution_update_old_field(this) + class(solution_type), intent(inout) :: this + this%un = this%u ! 与Julia的 un .= u 一致 + end subroutine solution_update_old_field + + subroutine solution_reset_instance(this) + class(solution_type), intent(inout) :: this + call solution_reset(this) + end subroutine solution_reset_instance + + subroutine solution_reset(solution) + type(solution_type), intent(inout) :: solution + + if (allocated(solution%u)) solution%u = 0.0_wp + if (allocated(solution%un)) solution%un = 0.0_wp + if (allocated(solution%q_face_left)) solution%q_face_left = 0.0_wp + if (allocated(solution%q_face_right)) solution%q_face_right = 0.0_wp + if (allocated(solution%flux)) solution%flux = 0.0_wp + if (allocated(solution%res)) solution%res = 0.0_wp + + if (associated(solution%domain) .and. solution%domain%config%verbose) then + print *, "[SOLUTION] Reset" + end if + end subroutine solution_reset + + subroutine solution_print_info(this) + class(solution_type), intent(in) :: this + + print *, "=== Solution Information ===" + print *, "Arrays:" + print *, " u: ", size(this%u), " elements" + print *, " un: ", size(this%un), " elements" + print *, " q_face_left: ", size(this%q_face_left), " elements" + print *, " q_face_right: ", size(this%q_face_right), " elements" + print *, " flux: ", size(this%flux), " elements" + print *, " res: ", size(this%res), " elements" + + if (allocated(this%u)) then + print *, "Values:" + print *, " u min/max: ", minval(this%u), maxval(this%u) + print *, " un min/max: ", minval(this%un), maxval(this%un) + end if + print *, "============================" + end subroutine solution_print_info + +end module solution_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/manager/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02h/src/manager/CMakeLists.txt new file mode 100644 index 00000000..00c8bf49 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/manager/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/manager/CMakeLists.txt +message(STATUS "配置管理器模块...") + +# 创建管理器库 +add_library(manager STATIC + component_manager.f90 + component_factory.f90 +) + +# 明确依赖关系:管理器依赖所有其他模块 +target_link_libraries(manager + PRIVATE + core + infrastructure + reconstructor + flux +) + +# 设置模块输出目录 +set_target_properties(manager PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "管理器模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/manager/component_factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/src/manager/component_factory.f90 new file mode 100644 index 00000000..114fedea --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/manager/component_factory.f90 @@ -0,0 +1,127 @@ +! src/manager/component_factory.f90 +module component_factory_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + use eno_reconstructor_module, only: eno_reconstructor, create_eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor, create_weno3_reconstructor + use rusanov_flux_module, only: rusanov_flux, create_rusanov_flux + + implicit none + private + public :: wp, create_reconstructor, create_flux_calculator + + ! 错误代码 + integer, parameter :: CM_SUCCESS = 0 + integer, parameter :: CM_ERROR_UNKNOWN_SCHEME = 1 + integer, parameter :: CM_ERROR_UNKNOWN_FLUX = 2 + integer, parameter :: CM_ERROR_INVALID_ORDER = 3 + +contains + + ! ==================== 重构器创建 ==================== + + function create_reconstructor(config, status) result(recon) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(reconstructor_base), allocatable :: recon + + character(len=20) :: scheme + integer :: order, error_code + + scheme = trim(adjustl(config%recon_scheme)) + order = config%spatial_order + + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating reconstructor: ", scheme, " order=", order + end if + + select case(scheme) + case('eno') + allocate(eno_reconstructor :: recon) + select type(recon) + type is(eno_reconstructor) + recon = create_eno_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case('weno3') + allocate(weno3_reconstructor :: recon) + select type(recon) + type is(weno3_reconstructor) + recon = create_weno3_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case default + error_code = CM_ERROR_UNKNOWN_SCHEME + if (config%verbose) then + print *, "[ERROR] Unknown reconstructor scheme: ", scheme + print *, " Available: eno, weno3" + end if + end select + + ! 检查阶数有效性 + if (error_code == CM_SUCCESS) then + if (order < 1) then + error_code = CM_ERROR_INVALID_ORDER + if (config%verbose) then + print *, "[ERROR] Invalid spatial order: ", order + end if + end if + end if + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Reconstructor creation failed" + end if + end function create_reconstructor + + ! ==================== 通量计算器创建 ==================== + + function create_flux_calculator(config, status) result(flux) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(flux_calculator_base), allocatable :: flux + + character(len=20) :: flux_type + integer :: error_code + + flux_type = trim(adjustl(config%flux_type)) + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating flux calculator: ", flux_type + end if + + select case(flux_type) + case('rusanov') + allocate(rusanov_flux :: flux) + select type(flux) + type is(rusanov_flux) + flux = create_rusanov_flux() + flux%wave_speed_default = config%wave_speed + end select + + case default + error_code = CM_ERROR_UNKNOWN_FLUX + if (config%verbose) then + print *, "[ERROR] Unknown flux type: ", flux_type + print *, " Available: rusanov" + end if + end select + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Flux calculator creation failed" + end if + end function create_flux_calculator + +end module component_factory_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/manager/component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/src/manager/component_manager.f90 new file mode 100644 index 00000000..9e095c25 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/manager/component_manager.f90 @@ -0,0 +1,75 @@ +! src/manager/component_manager.f90 +module component_manager_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use component_factory_module, only: create_reconstructor, create_flux_calculator + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + implicit none + private + public :: wp, component_manager_info, validate_config + public :: create_reconstructor, create_flux_calculator + +contains + + ! ==================== 配置验证 ==================== + + function validate_config(config) result(is_valid) + type(cfd_config), intent(in) :: config + logical :: is_valid + + integer :: status + class(reconstructor_base), allocatable :: test_recon + class(flux_calculator_base), allocatable :: test_flux + + is_valid = .false. + + ! 测试创建重构器 + test_recon = create_reconstructor(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid reconstructor configuration" + end if + return + end if + + ! 测试创建通量计算器 + test_flux = create_flux_calculator(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid flux configuration" + end if + return + end if + + ! 清理测试组件 + if (allocated(test_recon)) deallocate(test_recon) + if (allocated(test_flux)) deallocate(test_flux) + + is_valid = .true. + + if (config%verbose) then + print *, "[CONFIG VALIDATION] Configuration is valid" + end if + end function validate_config + + ! ==================== 信息显示 ==================== + + subroutine component_manager_info() + print *, "=== Component Manager ===" + print *, "Available reconstructors:" + print *, " - eno (orders: 1-7)" + print *, " - weno3 (order: 3)" + print *, "" + print *, "Available flux calculators:" + print *, " - rusanov" + print *, "" + print *, "Features:" + print *, " - Configuration validation" + print *, " - Component creation from config" + print *, " - Error handling with status codes" + print *, "=========================" + end subroutine component_manager_info + +end module component_manager_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02h/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02h/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..3fde7746 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,28 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux STATIC + base.f90 + rusanov.f90 +) + +#target_link_libraries(flux PRIVATE core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 明确设置不使用 /Qm64 选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + set_target_properties(flux PROPERTIES + Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /Qm64" # 明确设置,避免继承问题 + ) +endif() + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/src/numerics/flux/base.f90 new file mode 100644 index 00000000..7080a7ae --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/numerics/flux/base.f90 @@ -0,0 +1,30 @@ +!src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + procedure :: print_basic_info => flux_print_basic ! 添加辅助方法 + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + + subroutine flux_print_basic(this) + class(flux_calculator_base), intent(in) :: this + print *, " Name: ", trim(this%name) + end subroutine flux_print_basic + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..daa9e3bb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/numerics/flux/rusanov.f90 @@ -0,0 +1,41 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux, create_rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + + ! 构造函数接口 + interface rusanov_flux + module procedure create_rusanov_flux + end interface + +contains + + ! 构造函数 + type(rusanov_flux) function create_rusanov_flux() result(this) + this%name = "Rusanov" + this%wave_speed_default = 1.0_real64 + end function create_rusanov_flux + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Flux calculator information:" + call this%print_basic_info() + + ! 添加Rusanov特有信息 + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02h/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..5e4b938d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,22 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor STATIC + base.f90 + eno.f90 + weno3.f90 +) + +target_link_libraries(reconstructor PRIVATE core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..53798d02 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/numerics/reconstructor/base.f90 @@ -0,0 +1,33 @@ +!src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + procedure :: print_basic_info => reconstructor_print_basic ! 添加一个辅助方法 + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + + subroutine reconstructor_print_basic(this) + class(reconstructor_base), intent(in) :: this + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_print_basic + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..f973e8b3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor, create_eno_reconstructor ! ← 添加这个 + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + + ! 构造函数接口 + interface eno_reconstructor + module procedure create_eno_reconstructor + end interface + +contains + + ! 构造函数 + type(eno_reconstructor) function create_eno_reconstructor() result(this) + this%name = "ENO" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_eno_reconstructor + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加ENO特有信息 + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..d5b7a747 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno3_reconstructor, create_weno3_reconstructor + + type, extends(reconstructor_base) :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + + ! 构造函数接口 + interface weno3_reconstructor + module procedure create_weno3_reconstructor + end interface + +contains + + ! 构造函数 + type(weno3_reconstructor) function create_weno3_reconstructor() result(this) + this%name = "WENO3" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_weno3_reconstructor + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO3特有信息 + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/physics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02h/src/physics/CMakeLists.txt new file mode 100644 index 00000000..cc4e233a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/physics/CMakeLists.txt @@ -0,0 +1,19 @@ +# src/physics/CMakeLists.txt +message(STATUS "配置物理模块...") + +# 创建物理模块库 +add_library(physics STATIC + physics_interface.f90 + equations/linear_convection.f90 + problems/linear_convection_problem.f90 +) + +# 链接依赖 +target_link_libraries(physics PRIVATE base) + +# 设置模块输出目录 +set_target_properties(physics PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "物理模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/physics/equations/linear_convection.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/src/physics/equations/linear_convection.f90 new file mode 100644 index 00000000..595a2110 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/physics/equations/linear_convection.f90 @@ -0,0 +1,83 @@ +! src/physics/equations/linear_convection.f90 +module linear_convection_equation + use precision_module, only: wp, ip + use physics_interface, only: physics_equation + implicit none + private + + public :: linear_convection_eq, create_linear_convection_eq + public :: linear_convection_flux, linear_convection_speed + + ! 具体方程类型 + type, extends(physics_equation) :: linear_convection_eq + real(wp) :: wave_speed = 1.0_wp + contains + procedure :: flux => lc_flux + procedure :: speed => lc_speed + end type linear_convection_eq + + ! 独立函数(供外部调用) + interface linear_convection_flux + module procedure :: lc_flux_func + end interface + + interface linear_convection_speed + module procedure :: lc_speed_func + end interface + +contains + + ! 构造函数 + function create_linear_convection_eq(wave_speed) result(eq) + real(wp), intent(in), optional :: wave_speed + type(linear_convection_eq) :: eq + + eq%name = "Linear Convection" + if (present(wave_speed)) then + eq%wave_speed = wave_speed + else + eq%wave_speed = 1.0_wp + end if + end function create_linear_convection_eq + + ! 方法实现 + pure function lc_flux(this, u) result(f) + class(linear_convection_eq), intent(in) :: this + real(wp), intent(in) :: u + real(wp) :: f + f = this%wave_speed * u + end function lc_flux + + pure function lc_speed(this) result(a) + class(linear_convection_eq), intent(in) :: this + real(wp) :: a + a = this%wave_speed + end function lc_speed + + ! 独立函数 + pure function lc_flux_func(u, wave_speed) result(f) + real(wp), intent(in) :: u + real(wp), intent(in), optional :: wave_speed + real(wp) :: f + real(wp) :: a + + if (present(wave_speed)) then + a = wave_speed + else + a = 1.0_wp + end if + f = a * u + end function lc_flux_func + + pure function lc_speed_func(wave_speed) result(a) + real(wp), intent(in), optional :: wave_speed + real(wp) :: a + + if (present(wave_speed)) then + a = wave_speed + else + a = 1.0_wp + end if + end function lc_speed_func + +end module linear_convection_equation \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/physics/physics_interface.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/src/physics/physics_interface.f90 new file mode 100644 index 00000000..55662e5c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/physics/physics_interface.f90 @@ -0,0 +1,64 @@ +! src/physics/physics_interface.f90 +module physics_interface + use precision_module, only: wp, ip + implicit none + private + + ! 定义抽象基类型 + type, abstract :: physics_equation + character(len=:), allocatable :: name + contains + procedure(eq_flux_abs), deferred :: flux + procedure(eq_speed_abs), deferred :: speed + end type physics_equation + + type, abstract :: physics_problem + character(len=:), allocatable :: name + contains + procedure(prob_ic_abs), deferred :: initial_condition + procedure(prob_bc_abs), deferred :: boundary_condition + procedure(prob_exact_abs), deferred :: exact_solution + end type physics_problem + + ! 抽象接口定义 + abstract interface + pure function eq_flux_abs(this, u) result(f) + import :: physics_equation, wp + class(physics_equation), intent(in) :: this + real(wp), intent(in) :: u + real(wp) :: f + end function eq_flux_abs + + pure function eq_speed_abs(this) result(a) + import :: physics_equation, wp + class(physics_equation), intent(in) :: this + real(wp) :: a + end function eq_speed_abs + + subroutine prob_ic_abs(this, x, u) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp), intent(out) :: u(:) + end subroutine prob_ic_abs + + subroutine prob_bc_abs(this, u, t) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(inout) :: u(:) + real(wp), intent(in), optional :: t + end subroutine prob_bc_abs + + function prob_exact_abs(this, x, t) result(u) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(in) :: x(:), t + real(wp), dimension(size(x)) :: u + end function prob_exact_abs + end interface + + ! 公开接口 + public :: physics_equation, physics_problem + public :: wp, ip + +end module physics_interface \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/src/physics/problems/linear_convection_problem.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/src/physics/problems/linear_convection_problem.f90 new file mode 100644 index 00000000..31210710 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/src/physics/problems/linear_convection_problem.f90 @@ -0,0 +1,116 @@ +! src/physics/problems/linear_convection_problem.f90 +module linear_convection_problem + use precision_module, only: wp, ip + use physics_interface, only: physics_problem + implicit none + private + + public :: linear_convection_prob, create_linear_convection_prob + + ! 具体问题类型 + type, extends(physics_problem) :: linear_convection_prob + real(wp) :: wave_speed = 1.0_wp + real(wp) :: domain_length = 2.0_wp + character(len=20) :: ic_type = "step" + character(len=20) :: boundary_type = "periodic" + contains + procedure :: initial_condition => lc_initial_condition + procedure :: boundary_condition => lc_boundary_condition + procedure :: exact_solution => lc_exact_solution + end type linear_convection_prob + +contains + + ! 构造函数 + function create_linear_convection_prob(wave_speed, domain_length, & + ic_type, boundary_type) result(prob) + real(wp), intent(in), optional :: wave_speed, domain_length + character(len=*), intent(in), optional :: ic_type, boundary_type + type(linear_convection_prob) :: prob + + prob%name = "Linear Convection Problem" + + if (present(wave_speed)) prob%wave_speed = wave_speed + if (present(domain_length)) prob%domain_length = domain_length + if (present(ic_type)) prob%ic_type = ic_type + if (present(boundary_type)) prob%boundary_type = boundary_type + end function create_linear_convection_prob + + ! 初始条件 + subroutine lc_initial_condition(this, x, u) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp), intent(out) :: u(:) + + integer :: i + + select case (trim(this%ic_type)) + case ("step") + do i = 1, size(x) + if (x(i) >= 0.5_wp .and. x(i) <= 1.0_wp) then + u(i) = 2.0_wp + else + u(i) = 1.0_wp + end if + end do + + case ("sin", "sine") + do i = 1, size(x) + u(i) = sin(2.0_wp * 3.141592653589793_wp * x(i) / this%domain_length) + end do + + case ("gaussian") + do i = 1, size(x) + u(i) = exp(-((x(i) - 0.5_wp) / 0.1_wp)**2) + end do + + case default + ! 默认阶跃函数 + do i = 1, size(x) + if (x(i) >= 0.5_wp .and. x(i) <= 1.0_wp) then + u(i) = 2.0_wp + else + u(i) = 1.0_wp + end if + end do + end select + end subroutine lc_initial_condition + + ! 边界条件(虚拟实现,实际在boundary模块) + subroutine lc_boundary_condition(this, u, t) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(inout) :: u(:) + real(wp), intent(in), optional :: t + + ! 边界条件将在独立模块实现 + print *, "[PROBLEM] Boundary condition placeholder" + if (present(t)) then + print *, " Time = ", t + end if + end subroutine lc_boundary_condition + + ! 精确解(周期性平移) + function lc_exact_solution(this, x, t) result(u) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(in) :: x(:), t + real(wp), dimension(size(x)) :: u + real(wp), dimension(size(x)) :: x_shifted + integer :: i + + ! 周期性平移 + do i = 1, size(x) + x_shifted(i) = x(i) - this%wave_speed * t + ! 确保在 [0, domain_length) 范围内 + do while (x_shifted(i) < 0.0_wp) + x_shifted(i) = x_shifted(i) + this%domain_length + end do + do while (x_shifted(i) >= this%domain_length) + x_shifted(i) = x_shifted(i) - this%domain_length + end do + end do + + ! 重用初始条件函数 + call this%initial_condition(x_shifted, u) + end function lc_exact_solution + +end module linear_convection_problem \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/02h/tests/CMakeLists.txt new file mode 100644 index 00000000..ebbfc4f9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/tests/CMakeLists.txt @@ -0,0 +1,66 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +# +#message(STATUS "CMAKE_Fortran_MODULE_DIRECTORY=${CMAKE_Fortran_MODULE_DIRECTORY}") +# +add_executable(test_minimal_simple test_minimal_simple.f90) +target_link_libraries(test_minimal_simple + PRIVATE + core # 必须链接core库 + infrastructure +) + + +add_executable(test_simple_link test_simple_link.f90) +target_link_libraries(test_simple_link + PRIVATE + reconstructor + flux +) + + +add_executable(test_factory_simple test_factory_simple.f90) +target_link_libraries(test_factory_simple + PRIVATE + core + infrastructure + reconstructor + flux +) + + +add_executable(test_component_manager test_component_manager.f90) + +target_link_libraries(test_component_manager + PRIVATE + manager # ← 链接到新的管理器库 + infrastructure +) +add_executable(test_basic_only test_basic_only.f90) +target_link_libraries(test_basic_only + PRIVATE + infrastructure + core +) + +add_executable(test_physics_minimal test_physics_minimal.f90) +target_link_libraries(test_physics_minimal + PRIVATE + physics + base +) + +add_executable(test_domain_solution test_domain_solution.f90) +target_link_libraries(test_domain_solution + PRIVATE + infrastructure + core +) + +add_executable(test_config_physics test_config_physics.f90) +target_link_libraries(test_config_physics + PRIVATE + infrastructure + core +) diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_basic_only.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_basic_only.f90 new file mode 100644 index 00000000..20901ddc --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_basic_only.f90 @@ -0,0 +1,56 @@ +! tests/test_basic_only.f90 +program test_basic_only + ! 只测试最基本的功能,不依赖复杂模块 + use config_module, only: cfd_config, config_print, wp + use mesh_module, only: mesh_type + use registry_module, only: registry_init, registry_cleanup, & + register_component_simple, list_components + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "=== BASIC TEST - Minimal Functionality ===" + print *, "" + + ! 测试1: 配置 + print *, "1. Testing configuration..." + print *, "----------------------------" + call config_print(config) + print *, "" + + ! 测试2: 网格 + print *, "2. Testing mesh..." + print *, "------------------" + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=5) + print *, "Mesh initialized:" + print *, " Cells: ", mesh%ncells + print *, " Nodes: ", mesh%nnodes + print *, " dx: ", mesh%dx + print *, "" + + ! 测试3: 注册系统 + print *, "3. Testing registry..." + print *, "----------------------" + + call registry_init() + + ! 注册组件(使用简化版本) + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! 列出组件 + call list_components() + print *, "" + + ! 清理 + call registry_cleanup() + + print *, "=== TEST PASSED ===" + print *, "✓ Configuration works" + print *, "✓ Mesh works" + print *, "✓ Registry works" + +end program test_basic_only \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_cfd_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_cfd_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_cfd_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_component_manager.f90 new file mode 100644 index 00000000..f60c3505 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_component_manager.f90 @@ -0,0 +1,111 @@ +! tests/test_component_manager.f90 +program test_component_manager + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use component_manager_module, only: create_reconstructor, create_flux_calculator + use component_manager_module, only: component_manager_info, validate_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + implicit none + + type(cfd_config) :: config + class(reconstructor_base), allocatable :: recon + class(flux_calculator_base), allocatable :: flux + integer :: status + logical :: is_valid + + print *, "=== Component Manager Test ===" + print *, "" + + ! 显示组件管理器信息 + call component_manager_info() + print *, "" + + ! 测试1: 基本配置 + print *, "1. Testing basic ENO3 + Rusanov configuration..." + print *, "-----------------------------------------------" + + config%verbose = .true. + call config_print(config) + + ! 配置ENO3重构 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + call config_print(config) + print *, "" + + ! 验证配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Configuration is valid" + else + print *, "[ERROR] Configuration is invalid" + end if + print *, "" + + ! 测试2: 创建组件 + print *, "2. Testing component creation..." + print *, "--------------------------------" + + ! 创建重构器(带状态检查) + recon = create_reconstructor(config, status) + if (status == 0) then + print *, "[OK] Reconstructor created successfully" + call recon%info() + else + print *, "[ERROR] Failed to create reconstructor, code:", status + end if + print *, "" + + ! 创建通量计算器 + flux = create_flux_calculator(config, status) + if (status == 0) then + print *, "[OK] Flux calculator created successfully" + call flux%info() + else + print *, "[ERROR] Failed to create flux calculator, code:", status + end if + print *, "" + + ! 测试3: WENO3重构测试 + print *, "3. Testing WENO3 configuration..." + print *, "---------------------------------" + + call config_with_reconstruction(config, "weno3", 3) + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] WENO3 configuration is valid" + + ! 创建WENO3重构器 + recon = create_reconstructor(config) + call recon%info() + else + print *, "[ERROR] WENO3 configuration is invalid" + end if + print *, "" + + ! 测试4: 错误配置测试 + print *, "4. Testing invalid configuration..." + print *, "-----------------------------------" + + config%recon_scheme = "unknown_scheme" + config%flux_type = "unknown_flux" + + is_valid = validate_config(config) + if (.not. is_valid) then + print *, "[OK] Invalid configuration correctly rejected" + else + print *, "[ERROR] Invalid configuration should have been rejected" + end if + + ! 清理 + if (allocated(recon)) deallocate(recon) + if (allocated(flux)) deallocate(flux) + + print *, "" + print *, "=== Component manager test completed successfully ===" + +end program test_component_manager \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_config_physics.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_config_physics.f90 new file mode 100644 index 00000000..c6fef5c0 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_config_physics.f90 @@ -0,0 +1,141 @@ +! tests/test_config_physics.f90 (修复版) +program test_config_physics + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + + implicit none + + type(cfd_config) :: config + + print *, "=== Configuration Physics Test (Simplified) ===" + print *, "" + + ! 测试1: 默认配置 + print *, "1. Testing default configuration..." + print *, "-----------------------------------" + call config_print(config) + print *, "" + + ! 测试2: 验证基础物理字段 + print *, "2. Testing basic physics fields..." + print *, "----------------------------------" + + print *, "Verifying default physics fields:" + + if (trim(config%equation_type) == "linear_advection") then + print *, " ✓ Default equation type: linear_advection" + else + print *, " ✗ Unexpected equation type: ", trim(config%equation_type) + end if + + if (trim(config%problem_type) == "linear_advection") then + print *, " ✓ Default problem type: linear_advection" + else + print *, " ✗ Unexpected problem type: ", trim(config%problem_type) + end if + + if (abs(config%domain_length - 2.0_wp) < 1e-10_wp) then + print *, " ✓ Default domain length: 2.0" + else + print *, " ✗ Unexpected domain length: ", config%domain_length + end if + + if (config%enable_physics) then + print *, " ✓ Physics enabled by default" + else + print *, " ✗ Physics not enabled by default" + end if + + print *, "" + + ! 测试3: 使用类型绑定的方法(正确的方法名) + print *, "3. Testing type-bound procedures..." + print *, "--------------------------------------" + + call config%set_physics_parameters( & + equation_type="burgers_equation", & + problem_type="sod_shock_tube", & + domain_length=3.0_wp, & + enable_physics=.false.) + + print *, "After set_physics_parameters:" + print *, " Equation type: ", trim(config%equation_type) + print *, " Problem type: ", trim(config%problem_type) + print *, " Domain length: ", config%domain_length + print *, " Physics enabled: ", config%enable_physics + + if (trim(config%equation_type) == "burgers_equation") then + print *, " ✓ Equation type modified successfully via set_physics_parameters" + end if + + if (trim(config%problem_type) == "sod_shock_tube") then + print *, " ✓ Problem type modified successfully via set_physics_parameters" + end if + + if (abs(config%domain_length - 3.0_wp) < 1e-10_wp) then + print *, " ✓ Domain length modified successfully via set_physics_parameters" + end if + + if (.not. config%enable_physics) then + print *, " ✓ Physics disabled successfully via set_physics_parameters" + end if + + print *, "" + + ! 测试4: 调用get_physics_info方法 + print *, "4. Testing get_physics_info method..." + print *, "--------------------------------------" + call config%get_physics_info() + print *, "" + + ! 测试5: 高斯脉冲配置 + print *, "5. Testing Gaussian pulse configuration..." + print *, "-----------------------------------------" + + config%ic_type = "gaussian" + config%pulse_center = 0.6_wp + config%pulse_width = 0.15_wp + + print *, "Gaussian pulse parameters:" + print *, " IC type: ", trim(config%ic_type) + print *, " Center: ", config%pulse_center + print *, " Width: ", config%pulse_width + + if (trim(config%ic_type) == "gaussian") then + print *, " ✓ Gaussian IC type set" + end if + + if (abs(config%pulse_center - 0.6_wp) < 1e-10_wp) then + print *, " ✓ Pulse center set" + end if + + if (abs(config%pulse_width - 0.15_wp) < 1e-10_wp) then + print *, " ✓ Pulse width set" + end if + + print *, "" + + ! 测试6: 重构配置 + print *, "6. Testing reconstruction configuration..." + print *, "------------------------------------------" + + call config_with_reconstruction(config, "weno", 5) + + print *, "Reconstruction configuration:" + print *, " Scheme: ", trim(config%recon_scheme) + print *, " Order: ", config%spatial_order + + if (trim(config%recon_scheme) == "weno" .and. config%spatial_order == 5) then + print *, " ✓ WENO5 configuration successful" + else + print *, " ✗ Reconstruction configuration failed" + end if + + print *, "" + + print *, "=== Configuration Physics Test Complete ===" + print *, "✓ Config module updated with physics support" + print *, "✓ Fields can be directly accessed and modified" + print *, "✓ Type-bound procedures work correctly" + +end program test_config_physics \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_domain_solution.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_domain_solution.f90 new file mode 100644 index 00000000..ff659bac --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_domain_solution.f90 @@ -0,0 +1,102 @@ +! tests/test_domain_solution.f90 +program test_domain_solution + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create, solution_reset + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + real(wp), allocatable :: initial_values(:) + integer :: i + + print *, "=== Domain and Solution Test ===" + print *, "" + + ! 测试1: 不同重构方案的ghost层计算 + print *, "1. Testing ghost layer calculation..." + print *, "--------------------------------------" + + ! ENO3 + call config_with_reconstruction(config, "eno", 3) + config%verbose = .false. + call mesh%init(ncells=10) + domain = domain_create(config, mesh) + print *, "ENO3: nghosts = ", domain%nghosts, " (expected: 3)" + + ! WENO3 + call config_with_reconstruction(config, "weno3", 3) + domain = domain_create(config, mesh) + print *, "WENO3: nghosts = ", domain%nghosts, " (expected: 2)" + + ! WENO5 + call config_with_reconstruction(config, "weno", 5) + domain = domain_create(config, mesh) + print *, "WENO5: nghosts = ", domain%nghosts, " (expected: 3)" + print *, "" + + ! 测试2: Solution数组 + print *, "2. Testing solution arrays..." + print *, "------------------------------" + + call config_with_reconstruction(config, "eno", 3) + config%verbose = .true. + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + domain = domain_create(config, mesh) + call domain%print_info() + print *, "" + + solution = solution_create(domain) + call solution%print_info() + print *, "" + + ! 测试3: 初始化和更新 + print *, "3. Testing initialization and update..." + print *, "----------------------------------------" + + allocate(initial_values(mesh%ncells)) + do i = 1, mesh%ncells + initial_values(i) = sin(2.0_wp * 3.14159265358979_wp * mesh%xcc(i) / mesh%L) + end do + + call solution%initialize(initial_values) + print *, "After initialization:" + print *, " u range: ", minval(solution%u), " to ", maxval(solution%u) + print *, " un range: ", minval(solution%un), " to ", maxval(solution%un) + + ! 修改当前解,测试更新 + solution%u = solution%u * 2.0_wp + call solution%update_old_field() + print *, "After update: max|u - un| = ", maxval(abs(solution%u - solution%un)) + print *, "" + + ! 测试4: 重置 + print *, "4. Testing reset..." + print *, "-------------------" + + call solution_reset(solution) + print *, "After reset:" + print *, " u max: ", maxval(abs(solution%u)) + print *, " un max: ", maxval(abs(solution%un)) + print *, " flux max: ", maxval(abs(solution%flux)) + print *, "" + + deallocate(initial_values) + + print *, "=== Test Summary ===" + print *, "✓ Ghost layer calculation works" + print *, "✓ Domain creation works" + print *, "✓ Solution arrays work" + print *, "✓ Initialization works" + print *, "✓ Field update works" + print *, "✓ Reset works" + print *, "" + print *, "Ready for next step: Implementing Physics modules" + +end program test_domain_solution \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_factory_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_factory_simple.f90 new file mode 100644 index 00000000..db65da7c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_factory_simple.f90 @@ -0,0 +1,58 @@ +! tests/test_factory_simple.f90 (修复版) +program test_factory_simple + use base_modules, only: wp ! ← 添加这行 + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Basic systems + print *, "1. Testing basic systems..." + print *, "-----------------------------" + call config_print(config) + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Creating reconstructors + print *, "2. Testing reconstructors..." + print *, "------------------------------" + + ! 创建并测试ENO重构器 + print *, "Creating ENO reconstructor..." + eno = eno_reconstructor() ! 使用构造函数 + call eno%info() ! 必须调用info方法 + + print *, "" + print *, "Creating WENO3 reconstructor..." + weno3 = weno3_reconstructor() ! 使用构造函数 + call weno3%info() ! 必须调用info方法 + print *, "" + + ! Test 3: Creating flux calculator + print *, "3. Testing flux calculator..." + print *, "-------------------------------" + + print *, "Creating Rusanov flux calculator..." + rusanov = rusanov_flux() ! 使用构造函数 + call rusanov%info() ! 必须调用info方法 + print *, "" + + print *, "=== Factory pattern simple test completed successfully ===" + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_minimal_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_minimal_simple.f90 new file mode 100644 index 00000000..ec03ccf8 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_minimal_simple.f90 @@ -0,0 +1,87 @@ +! tests/test_minimal_simple.f90 +program test_minimal_simple + use base_modules, only: wp ! ← 添加这行 + use registry_module + use config_module + use mesh_module + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Simple Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call registry_init() + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call list_components() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + + if (has_component_simple("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component_simple("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components (simple version) + print *, "5. Testing registry functionality" + print *, "---------------------------------" + print *, "Registry initialized: ", registry_is_initialized() + print *, "Number of components: ", registry_get_size() + print *, "" + + ! Cleanup + call registry_cleanup() + + print *, "=== Simple test completed successfully ===" + +end program test_minimal_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_physics_minimal.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_physics_minimal.f90 new file mode 100644 index 00000000..cf2a28f1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_physics_minimal.f90 @@ -0,0 +1,91 @@ +! tests/test_physics_minimal.f90 +program test_physics_minimal + use precision_module, only: wp, ip + use linear_convection_equation, only: linear_convection_eq, create_linear_convection_eq + use linear_convection_problem, only: linear_convection_prob, create_linear_convection_prob + + implicit none + + type(linear_convection_eq) :: eq + type(linear_convection_prob) :: prob + real(wp) :: u, f, a + real(wp), allocatable :: x(:), u_ic(:), u_exact(:) + integer :: i, nx = 10 + + print *, "=== 最小物理模块测试 ===" + print *, "" + + ! 测试1: 方程功能 + print *, "1. 测试方程功能..." + print *, "-------------------" + + eq = create_linear_convection_eq(wave_speed=2.0_wp) + print *, "方程: ", eq%name + print *, "波速: ", eq%wave_speed + + u = 1.5_wp + f = eq%flux(u) + a = eq%speed() + + print *, "u = ", u + print *, "F(u) = ", f, " (期望: 3.0)" + print *, "波速 a = ", a, " (期望: 2.0)" + + if (abs(f - 3.0_wp) < 1e-10_wp .and. abs(a - 2.0_wp) < 1e-10_wp) then + print *, "✓ 方程功能正常" + else + print *, "✗ 方程功能异常" + end if + print *, "" + + ! 测试2: 问题功能 + print *, "2. 测试问题功能..." + print *, "-------------------" + + prob = create_linear_convection_prob(ic_type="step", domain_length=2.0_wp) + print *, "问题: ", prob%name + print *, "IC类型: ", trim(prob%ic_type) + print *, "域长度: ", prob%domain_length + + allocate(x(nx), u_ic(nx), u_exact(nx)) + do i = 1, nx + x(i) = 0.0_wp + (i-1) * 0.2_wp + end do + + ! 测试初始条件 + call prob%initial_condition(x, u_ic) + print *, "初始条件范围: ", minval(u_ic), " 到 ", maxval(u_ic) + + ! 测试精确解 + u_exact = prob%exact_solution(x, 0.0_wp) + print *, "t=0时精确解范围: ", minval(u_exact), " 到 ", maxval(u_exact) + + ! 检查阶跃函数 + if (abs(u_ic(1) - 1.0_wp) < 1e-10_wp .and. & + abs(u_ic(6) - 2.0_wp) < 1e-10_wp) then + print *, "✓ 阶跃初始条件正确" + else + print *, "✗ 阶跃初始条件错误" + end if + + ! 检查精确解与初始条件一致 + if (maxval(abs(u_ic - u_exact)) < 1e-10_wp) then + print *, "✓ t=0时精确解与初始条件一致" + else + print *, "✗ 精确解计算错误" + end if + print *, "" + + ! 测试3: 边界条件接口 + print *, "3. 测试边界条件接口..." + print *, "----------------------" + call prob%boundary_condition(u_ic, 0.0_wp) + print *, "✓ 边界条件接口正常" + print *, "" + + deallocate(x, u_ic, u_exact) + + print *, "=== 物理模块最小测试完成 ===" + print *, "下一步: 将物理模块集成到现有系统中" + +end program test_physics_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_simple_link.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_simple_link.f90 new file mode 100644 index 00000000..71cc614e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_simple_link.f90 @@ -0,0 +1,78 @@ +! tests/test_simple_link.f90 +program test_simple_link + use base_modules, only: wp ! ← 添加这行 + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call registry_init() + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call list_components() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component_simple("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component_simple("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Cleanup + call registry_cleanup() + + print *, "=== Minimal test completed successfully ===" + +end program test_simple_link \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_solver_framework.f90 b/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_solver_framework.f90 new file mode 100644 index 00000000..6754323d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/02h/tests/test_solver_framework.f90 @@ -0,0 +1,91 @@ +! tests/test_solver_framework.f90 +program test_solver_framework + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use mesh_module, only: mesh_type + use solver_module, only: cfd_solver, solver_create, solver_run, solver_cleanup + use solver_module, only: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, & + SOLVER_COMPLETED, SOLVER_ERROR + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(cfd_solver) :: solver + + print *, "=== 求解器框架测试 ===" + print *, "" + + ! 测试1: 基本创建 + print *, "1. 测试求解器创建..." + print *, "----------------------" + + ! 创建配置 + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.0_real64 + config%dt = 0.01_real64 + + call config_print(config) + print *, "" + + ! 创建网格 + call mesh%init(xmin=0.0_real64, xmax=2.0_real64, ncells=20) + call mesh%print_info() + print *, "" + + ! 创建求解器 + solver = solver_create(config, mesh) + print *, "✓ 求解器创建成功" + print *, " 状态: ", solver%get_state() + print *, "" + + ! 测试2: 求解器初始化 + print *, "2. 测试求解器初始化..." + print *, "------------------------" + + call solver%initialize() + print *, "✓ 求解器初始化完成" + print *, " 状态: ", solver%get_state() + print *, " 错误信息: '", trim(solver%get_error()), "'" + print *, "" + + ! 测试3: 简单运行 + print *, "3. 测试求解器运行..." + print *, "----------------------" + + call solver_run(solver, 0.05_real64) ! 运行到0.05秒 + print *, "✓ 求解器运行完成" + print *, " 最终状态: ", solver%get_state() + print *, "" + + ! 测试4: 清理 + print *, "4. 测试求解器清理..." + print *, "----------------------" + + call solver_cleanup(solver) + print *, "✓ 求解器清理完成" + print *, " 状态: ", solver%get_state() + print *, "" + + ! 测试5: 错误处理 + print *, "5. 测试错误处理..." + print *, "-------------------" + + ! 尝试重复初始化 + call solver%initialize() + print *, " 重复初始化状态: ", solver%get_state() + print *, " 错误信息: '", trim(solver%get_error()), "'" + + call solver_cleanup(solver) + print *, "" + + print *, "=== 框架测试总结 ===" + print *, "✓ 求解器创建/初始化/运行/清理流程验证完成" + print *, "✓ 状态管理正常工作" + print *, "✓ 错误处理机制就绪" + print *, "" + print *, "下一步: 添加实际数值计算功能" + +end program test_solver_framework \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03/CMakeLists.txt new file mode 100644 index 00000000..ef66d584 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/README.md b/example/1d-linear-convection/weno3/fortran/registry/03/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/03/scripts/build.bat new file mode 100644 index 00000000..6fd6dc03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/scripts/build.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Project Builder +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python build script with full Intel environment support... +echo. + +python build.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Build failed + pause + exit /b 1 +) + +echo. +echo [INFO] Build completed successfully! +echo. +echo [INFO] To run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/03/scripts/build.py new file mode 100644 index 00000000..3bf6d537 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/scripts/build.py @@ -0,0 +1,629 @@ +#!/usr/bin/env python3 +""" +Fortran CFD Project Builder - 完整Python解决方案 +在Python内部处理Intel oneAPI环境配置 +""" + +import os +import sys +import subprocess +import shutil +import argparse +import time +import platform +import tempfile +from pathlib import Path + +class IntelEnvironment: + """Intel oneAPI环境管理器""" + + def __init__(self): + self.setvars_path = None + self.env_vars = {} + + def find_setvars(self): + """查找setvars.bat文件""" + possible_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + r"C:\Program Files (x86)\Intel\oneAPI\compiler\latest\env\vars.bat", + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\setvars.bat"), + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\compiler\latest\env\vars.bat"), + ] + + for path in possible_paths: + if os.path.exists(path): + self.setvars_path = path + return True + + return False + + def setup_environment(self): + """设置Intel环境""" + if not self.find_setvars(): + return False + + try: + # 创建临时的批处理文件来捕获环境变量 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + f.write(f'@echo off\n') + f.write(f'call "{self.setvars_path}" >nul 2>&1\n') + f.write(f'set\n') # 输出所有环境变量 + temp_bat = f.name + + # 运行批处理文件并捕获输出 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + shell=True + ) + + # 解析环境变量 + for line in result.stdout.split('\n'): + line = line.strip() + if '=' in line: + key, value = line.split('=', 1) + self.env_vars[key.strip()] = value.strip() + + # 清理临时文件 + os.unlink(temp_bat) + + # 更新当前进程的环境变量 + os.environ.update(self.env_vars) + + return True + + except Exception as e: + print(f"设置Intel环境失败: {e}") + return False + + def get_compiler_info(self): + """获取编译器信息""" + info = {} + + # 检查ifx编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + env={**os.environ, **self.env_vars} if self.env_vars else os.environ + ) + + if result.returncode == 0: + for line in result.stdout.split('\n'): + if 'Version' in line or '版本' in line: + info['ifx_version'] = line.strip() + break + except: + pass + + # 检查环境变量 + info['ifx_root'] = self.env_vars.get('IFX_ROOT', '') + info['compiler_root'] = self.env_vars.get('ONEAPI_ROOT', '') + + return info + +class BuildSystem: + """构建系统主类""" + + def __init__(self): + self.project_root = Path(__file__).parent.parent + self.build_dir = self.project_root / "build" + self.intel_env = IntelEnvironment() + + # 设置控制台编码 + if sys.platform == "win32": + try: + import ctypes + # 设置控制台输出为UTF-8 + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + except: + pass + + def print_header(self, text): + """打印标题""" + print(f"\n{'='*70}") + print(f" {text}") + print(f"{'='*70}\n") + + def print_step(self, step, total, message): + """打印步骤""" + print(f"[{step}/{total}] {message}...") + + def print_success(self, message): + """打印成功""" + print(f"\033[92m✓ {message}\033[0m") + + def print_error(self, message): + """打印错误""" + print(f"\033[91m✗ {message}\033[0m") + + def print_warning(self, message): + """打印警告""" + print(f"\033[93m! {message}\033[0m") + + def print_info(self, message): + """打印信息""" + print(f"\033[94mℹ {message}\033[0m") + + def check_prerequisites(self): + """检查前提条件""" + self.print_step(1, 6, "检查前提条件") + + # 检查Python版本 + python_version = sys.version.split()[0] + self.print_info(f"Python版本: {python_version}") + + # 检查平台 + self.print_info(f"平台: {platform.system()} {platform.release()}") + self.print_info(f"处理器核心数: {os.cpu_count()}") + + # 检查CMake + try: + result = subprocess.run( + ["cmake", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + version_line = result.stdout.split('\n')[0] + self.print_success(f"CMake: {version_line}") + else: + self.print_error("CMake未找到") + return False + except FileNotFoundError: + self.print_error("CMake未安装") + return False + + return True + + def setup_intel_environment(self, args): + """设置Intel环境""" + self.print_step(2, 6, "配置Intel oneAPI环境") + + if not self.intel_env.find_setvars(): + self.print_warning("未找到Intel oneAPI setvars.bat") + self.print_info("将尝试使用系统环境中的编译器") + + # 检查是否能直接访问编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + self.print_success("Intel编译器在系统PATH中找到") + return True + else: + self.print_warning("Intel编译器未在PATH中找到") + except: + self.print_warning("无法访问Intel编译器") + + return True # 继续,让CMake自己找编译器 + + # 设置环境 + if self.intel_env.setup_environment(): + compiler_info = self.intel_env.get_compiler_info() + + if compiler_info.get('ifx_version'): + self.print_success(f"Intel Fortran编译器: {compiler_info['ifx_version']}") + elif compiler_info.get('ifx_root'): + self.print_success(f"Intel编译器路径: {compiler_info['ifx_root']}") + else: + self.print_success("Intel oneAPI环境配置完成") + + return True + else: + self.print_warning("Intel环境配置失败,将继续使用系统环境") + return True + + def clean_build_directory(self, args): + """清理构建目录""" + if args.clean and self.build_dir.exists(): + self.print_info("清理构建目录...") + try: + shutil.rmtree(self.build_dir) + self.print_success("构建目录已清理") + except Exception as e: + self.print_error(f"清理失败: {e}") + if not args.force: + return False + return True + + def run_command(self, cmd, cwd=None, check=True, env=None): + """运行命令""" + if isinstance(cmd, list): + cmd_str = ' '.join(str(c) for c in cmd if c) + else: + cmd_str = str(cmd) + + print(f" \033[96m$\033[0m {cmd_str}") + + try: + # 合并环境变量 + exec_env = os.environ.copy() + if env: + exec_env.update(env) + if self.intel_env.env_vars: + exec_env.update(self.intel_env.env_vars) + + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=False, + env=exec_env + ) + + # 处理输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or '完成' in line or '生成' in line: + print(f" \033[92m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + if check and result.returncode != 0: + self.print_error(f"命令执行失败,退出码: {result.returncode}") + return False + + return True + + except Exception as e: + self.print_error(f"命令执行异常: {e}") + return False + + def configure_cmake(self, args): + """配置CMake""" + self.print_step(3, 6, "配置CMake项目") + + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ] + + if args.compiler == "ifx": + cmake_cmd.extend(["-T", "fortran=ifx"]) + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + success = self.run_command(cmake_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("CMake配置完成") + else: + self.print_error("CMake配置失败") + + return success + + def build_project(self, args): + """构建项目""" + self.print_step(4, 6, "构建项目") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + success = self.run_command(build_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("项目构建完成") + else: + self.print_error("构建失败") + + return success + + def run_tests_with_environment(self, test_exe): + """运行单个测试,确保有Intel环境""" + try: + # 创建临时的批处理文件来运行测试 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'"{test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'"{test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + return result + + except Exception as e: + print(f"运行测试失败: {e}") + return None + + def run_tests(self, args): + """运行测试""" + self.print_step(5, 6, "运行测试") + + # 查找测试可执行文件 + test_dir = self.build_dir / "bin" / args.build_type + if not test_dir.exists(): + test_dir = self.build_dir / "bin" + if not test_dir.exists(): + test_dir = self.build_dir + + test_files = list(test_dir.glob("test_*.exe")) + + if not test_files: + self.print_warning("未找到测试程序") + return True + + all_passed = True + + for test_exe in sorted(test_files): + test_name = test_exe.stem + self.print_info(f"运行测试: {test_name}") + print(f" {'-'*50}") + + # 运行测试 + result = self.run_tests_with_environment(str(test_exe)) + + if result is None: + self.print_error(f" {test_name} 运行失败") + all_passed = False + continue + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + print(f" {line}") + + if result.returncode == 0: + self.print_success(f" {test_name} 通过") + else: + self.print_error(f" {test_name} 失败 (退出码: {result.returncode})") + all_passed = False + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + print() # 空行 + + return all_passed + + def create_test_runner(self, args): + """创建独立的测试运行器""" + self.print_step(6, 6, "创建测试运行器") + + runner_path = self.build_dir / "run_tests.bat" + + content = f'''@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Test Runner +echo ======================================== +echo. + +REM Setup Intel oneAPI environment +set "SETVARS_PATH={self.intel_env.setvars_path or ''}" +if exist "%SETVARS_PATH%" ( + call "%SETVARS_PATH%" >nul + echo [INFO] Intel environment configured +) else ( + echo [WARNING] Intel environment not found + echo [WARNING] Tests may fail without runtime libraries +) + +echo. + +REM Run all test executables +set "TEST_COUNT=0" +set "PASS_COUNT=0" + +for %%f in ("bin\\{args.build_type}\\test_*.exe") do ( + set /a TEST_COUNT+=1 + echo [TEST %%f] + echo {'-'*50} + + %%f + if errorlevel 1 ( + echo [FAILED] %%f + ) else ( + echo [PASSED] %%f + set /a PASS_COUNT+=1 + ) + echo. +) + +echo ======================================== +echo Tests: %PASS_COUNT%/%TEST_COUNT% passed +if %PASS_COUNT% equ %TEST_COUNT% ( + echo [SUCCESS] All tests passed! +) else ( + echo [FAILURE] Some tests failed +) +echo ======================================== + +pause +''' + + with open(runner_path, 'w', encoding='utf-8') as f: + f.write(content) + + self.print_success(f"测试运行器已创建: {runner_path}") + self.print_info(f"使用方法: cd build && run_tests.bat") + + return runner_path + + def generate_report(self, args, build_time, tests_passed): + """生成构建报告""" + self.print_header("构建完成") + + print(f"项目: {self.project_root.name}") + print(f"构建类型: {args.build_type}") + print(f"编译器: {args.compiler}") + print(f"并行作业: {args.jobs}") + print(f"总耗时: {build_time:.1f}秒") + print(f"测试结果: {'全部通过' if tests_passed else '有失败'}") + + # 显示生成的可执行文件 + bin_dir = self.build_dir / "bin" / args.build_type + if bin_dir.exists(): + print(f"\n生成的可执行文件:") + for exe in sorted(bin_dir.glob("*.exe")): + size_mb = exe.stat().st_size / (1024 * 1024) + print(f" • {exe.name} ({size_mb:.2f} MB)") + + # 显示测试运行器信息 + runner_path = self.build_dir / "run_tests.bat" + if runner_path.exists(): + print(f"\n独立测试运行器:") + print(f" • {runner_path.name}") + print(f" 在Intel oneAPI环境中运行所有测试") + + def run(self): + """运行构建系统""" + parser = argparse.ArgumentParser( + description="Fortran CFD项目构建工具", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认构建 + %(prog)s --clean # 清理后构建 + %(prog)s --build-type Release # Release构建 + %(prog)s --no-tests # 只构建,不运行测试 + %(prog)s -j8 --verbose # 8线程并行构建,详细输出 + """ + ) + + parser.add_argument("--build-type", choices=["Debug", "Release"], + default="Debug", help="构建类型") + parser.add_argument("--compiler", choices=["ifx", "ifort"], + default="ifx", help="Fortran编译器") + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-tests", action="store_true", + help="跳过测试") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + + args = parser.parse_args() + + # 开始构建 + start_time = time.time() + + self.print_header("Fortran CFD 项目构建系统") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 检查前提条件 + if not self.check_prerequisites(): + if not args.force: + return 1 + + # 2. 设置Intel环境 + if not self.setup_intel_environment(args): + if not args.force: + return 1 + + # 3. 清理目录 + if not self.clean_build_directory(args): + if not args.force: + return 1 + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 4. 配置CMake + if not self.configure_cmake(args): + if not args.force: + return 1 + + # 5. 构建项目 + if not self.build_project(args): + if not args.force: + return 1 + + # 6. 运行测试和创建测试运行器 + tests_passed = True + if not args.no_tests: + tests_passed = self.run_tests(args) + + # 创建测试运行器 + self.create_test_runner(args) # 传递 args 参数 + + # 7. 生成报告 + build_time = time.time() - start_time + self.generate_report(args, build_time, tests_passed) + + return 0 if tests_passed else 1 + + except KeyboardInterrupt: + self.print_error("\n构建被用户中断") + return 1 + except Exception as e: + self.print_error(f"构建过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + builder = BuildSystem() + return builder.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/03/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/scripts/run_all_steps.bat b/example/1d-linear-convection/weno3/fortran/registry/03/scripts/run_all_steps.bat new file mode 100644 index 00000000..d506149b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/scripts/run_all_steps.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo CFD Project: All Steps +echo ======================================== +echo. + +echo [INFO] Starting Step 1: Physics Modules Test... +call run_step1.bat + +if errorlevel 1 ( + echo [ERROR] Step 1 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Starting Step 2: Configuration Physics Update... +call run_step2.bat + +if errorlevel 1 ( + echo [ERROR] Step 2 failed + pause + exit /b 1 +) + +echo. +echo ======================================== +echo All Steps Completed Successfully! +echo ======================================== +echo. +echo [INFO] Next: Update component manager for physics support +echo [INFO] Run: run_step3.bat (to be created) +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/scripts/run_step1.bat b/example/1d-linear-convection/weno3/fortran/registry/03/scripts/run_step1.bat new file mode 100644 index 00000000..0b6b1f17 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/scripts/run_step1.bat @@ -0,0 +1,39 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Step 1: Physics Modules Test +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python step1 script with full Intel environment support... +echo. + +python run_step1.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Step 1 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Step 1 completed successfully! +echo. +echo [INFO] Next step: Update config to include physics settings +echo [INFO] Run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/scripts/run_step1.py b/example/1d-linear-convection/weno3/fortran/registry/03/scripts/run_step1.py new file mode 100644 index 00000000..5e087a69 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/scripts/run_step1.py @@ -0,0 +1,319 @@ +#!/usr/bin/env python3 +""" +Step 1: Physics Modules Test +扩展build.py,专门用于测试物理模块 +""" + +import os +import sys +import subprocess +import time +from pathlib import Path + +# 添加当前目录到路径,以便导入build.py的类 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import IntelEnvironment, BuildSystem +except ImportError: + print("Error: Cannot import build.py. Make sure build.py is in the same directory.") + sys.exit(1) + +class Step1System(BuildSystem): + """Step 1 测试系统,继承自BuildSystem""" + + def __init__(self): + super().__init__() + self.test_name = "test_physics_minimal" + self.test_exe = None + + def find_test_executable(self): + """查找测试可执行文件""" + possible_paths = [ + self.build_dir / "bin" / "Debug" / f"{self.test_name}.exe", + self.build_dir / "Debug" / f"{self.test_name}.exe", + self.build_dir / f"{self.test_name}.exe", + self.build_dir / "bin" / f"{self.test_name}.exe", + ] + + for path in possible_paths: + if path.exists(): + self.test_exe = path + self.print_success(f"Found test executable: {path}") + return True + + # 如果没有找到,尝试搜索 + self.print_warning(f"Could not find {self.test_name}.exe") + self.print_info("Searching for test executables...") + + try: + result = subprocess.run( + ["dir", str(self.build_dir), "/s", "/b", "*.exe"], + capture_output=True, + text=True, + encoding='utf-8', + shell=True + ) + + if result.returncode == 0: + test_files = [line.strip() for line in result.stdout.split('\n') + if line and 'test_' in line.lower()] + + if test_files: + self.print_info("Found test files:") + for test_file in test_files: + self.print_info(f" {test_file}") + return False + except: + pass + + return False + + def run_test_with_intel_env(self): + """在Intel环境下运行测试""" + if not self.test_exe: + self.print_error("No test executable found") + return False + + self.print_step(1, 2, f"Running test: {self.test_exe.name}") + + try: + # 创建临时的批处理文件来运行测试(包含Intel环境) + import tempfile + + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + # 设置Intel环境 + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'echo [INFO] Intel environment configured\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'echo [WARNING] Intel environment not found\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + self.print_info(f"Command: {temp_bat}") + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower() or 'fail' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or 'pass' in line.lower() or '✓' in line: + print(f" \033[92m{line}\033[0m") + elif '=' in line or '---' in line: + print(f" \033[96m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + self.print_warning("Test stderr output:") + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + self.print_step(2, 2, "Test execution completed") + + if result.returncode == 0: + self.print_success("Test passed") + return True + else: + self.print_error(f"Test failed (exit code: {result.returncode})") + return False + + except Exception as e: + self.print_error(f"Failed to run test: {e}") + return False + + def build_project_if_needed(self, args): + """如果需要,构建项目""" + if args.no_build: + self.print_info("Skipping build (--no-build flag)") + return True + + self.print_step(1, 3, "Building project") + + # 调用父类的构建方法 + build_args = argparse.Namespace() + build_args.clean = args.clean + build_args.build_type = "Debug" + build_args.compiler = "ifx" + build_args.no_tests = True # 不运行所有测试 + build_args.jobs = os.cpu_count() + build_args.verbose = args.verbose + build_args.force = args.force + + # 清理构建目录 + if args.clean and self.build_dir.exists(): + self.print_info("Cleaning build directory...") + import shutil + try: + shutil.rmtree(self.build_dir) + self.print_success("Build directory cleaned") + except Exception as e: + self.print_error(f"Clean failed: {e}") + if not args.force: + return False + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 配置CMake + self.print_step(2, 3, "Configuring CMake") + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-DCMAKE_BUILD_TYPE=Debug", + "-T", "fortran=ifx", + ] + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + if not self.run_command(cmake_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + # 构建项目 + self.print_step(3, 3, "Building project") + build_cmd = [ + "cmake", + "--build", ".", + "--config", "Debug", + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + if not self.run_command(build_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + self.print_success("Build completed") + return True + + def run(self): + """运行Step 1测试""" + parser = argparse.ArgumentParser( + description="Step 1: Physics Modules Test", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认运行 + %(prog)s --clean # 清理后构建并测试 + %(prog)s --no-build # 只运行测试,不重新构建 + %(prog)s --verbose # 详细输出 + %(prog)s -j4 # 使用4个并行作业构建 + """ + ) + + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-build", action="store_true", + help="不重新构建,直接运行测试") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + + args = parser.parse_args() + + # 开始测试 + start_time = time.time() + + self.print_header("Step 1: Physics Modules Implementation Test") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 设置Intel环境 + self.print_step(1, 4, "Setting up Intel oneAPI environment") + if not self.setup_intel_environment(args): + if not args.force: + self.print_error("Intel environment setup failed") + return 1 + self.print_warning("Intel environment setup failed, continuing...") + + # 2. 构建项目(如果需要) + self.print_step(2, 4, "Building project if needed") + if not self.build_project_if_needed(args): + if not args.force: + return 1 + + # 3. 查找测试可执行文件 + self.print_step(3, 4, "Finding test executable") + if not self.find_test_executable(): + if not args.force: + return 1 + self.print_warning("Test executable not found, but continuing due to --force") + return 0 + + # 4. 运行测试 + self.print_step(4, 4, "Running physics module test") + test_passed = self.run_test_with_intel_env() + + # 生成报告 + test_time = time.time() - start_time + self.print_header("Step 1 Complete") + + print(f"测试: {'通过 ✓' if test_passed else '失败 ✗'}") + print(f"测试程序: {self.test_exe.name if self.test_exe else '未找到'}") + print(f"总耗时: {test_time:.1f}秒") + + if test_passed: + print(f"\n下一步: 更新配置以包含物理设置") + print(f"建议: 修改config.f90,添加physics相关字段") + return 0 + else: + if args.force: + self.print_warning("测试失败,但由于--force标志继续执行") + return 0 + return 1 + + except KeyboardInterrupt: + self.print_error("\n测试被用户中断") + return 1 + except Exception as e: + self.print_error(f"测试过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + system = Step1System() + return system.run() + +if __name__ == "__main__": + # 需要导入argparse + import argparse + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/scripts/run_step2.bat b/example/1d-linear-convection/weno3/fortran/registry/03/scripts/run_step2.bat new file mode 100644 index 00000000..9c1f62de --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/scripts/run_step2.bat @@ -0,0 +1,39 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Step 2: Configuration Physics Update +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python step2 script with full Intel environment support... +echo. + +python run_step2.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Step 2 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Step 2 completed successfully! +echo. +echo [INFO] Next step: Update component manager to support physics +echo [INFO] Run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/scripts/run_step2.py b/example/1d-linear-convection/weno3/fortran/registry/03/scripts/run_step2.py new file mode 100644 index 00000000..c16b7608 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/scripts/run_step2.py @@ -0,0 +1,284 @@ +#!/usr/bin/env python3 +""" +Step 2: Configuration Physics Update +测试配置模块的物理功能更新 +""" + +import os +import sys +import subprocess +import time +from pathlib import Path + +# 添加当前目录到路径,以便导入build.py的类 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import IntelEnvironment, BuildSystem +except ImportError: + print("Error: Cannot import build.py. Make sure build.py is in the same directory.") + sys.exit(1) + +class Step2System(BuildSystem): + """Step 2 测试系统,继承自BuildSystem""" + + def __init__(self): + super().__init__() + self.test_name = "test_config_physics" + self.test_exe = None + + def find_test_executable(self): + """查找测试可执行文件""" + possible_paths = [ + self.build_dir / "bin" / "Debug" / f"{self.test_name}.exe", + self.build_dir / "Debug" / f"{self.test_name}.exe", + self.build_dir / f"{self.test_name}.exe", + self.build_dir / "bin" / f"{self.test_name}.exe", + ] + + for path in possible_paths: + if path.exists(): + self.test_exe = path + self.print_success(f"Found test executable: {path}") + return True + + return False + + def run_test_with_intel_env(self): + """在Intel环境下运行测试""" + if not self.test_exe: + self.print_error("No test executable found") + return False + + self.print_step(1, 2, f"Running test: {self.test_exe.name}") + + try: + # 创建临时的批处理文件来运行测试(包含Intel环境) + import tempfile + + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + # 设置Intel环境 + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'echo [INFO] Intel environment configured\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'echo [WARNING] Intel environment not found\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + self.print_info(f"Command: {temp_bat}") + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower() or 'fail' in line.lower() or '✗' in line: + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or 'pass' in line.lower() or '✓' in line: + print(f" \033[92m{line}\033[0m") + elif '=' in line or '---' in line or '===' in line: + print(f" \033[96m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + self.print_warning("Test stderr output:") + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + self.print_step(2, 2, "Test execution completed") + + if result.returncode == 0: + self.print_success("Test passed") + return True + else: + self.print_error(f"Test failed (exit code: {result.returncode})") + return False + + except Exception as e: + self.print_error(f"Failed to run test: {e}") + return False + + def build_project(self, args): + """构建项目""" + if args.no_build: + self.print_info("Skipping build (--no-build flag)") + return True + + self.print_step(1, 3, "Building project") + + # 清理构建目录 + if args.clean and self.build_dir.exists(): + self.print_info("Cleaning build directory...") + import shutil + try: + shutil.rmtree(self.build_dir) + self.print_success("Build directory cleaned") + except Exception as e: + self.print_error(f"Clean failed: {e}") + if not args.force: + return False + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 配置CMake + self.print_step(2, 3, "Configuring CMake") + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-DCMAKE_BUILD_TYPE=Debug", + "-T", "fortran=ifx", + ] + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + if not self.run_command(cmake_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + # 构建项目 + self.print_step(3, 3, "Building project") + build_cmd = [ + "cmake", + "--build", ".", + "--config", "Debug", + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + if not self.run_command(build_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + self.print_success("Build completed") + return True + + def run(self): + """运行Step 2测试""" + import argparse + + parser = argparse.ArgumentParser( + description="Step 2: Configuration Physics Update", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认运行 + %(prog)s --clean # 清理后构建并测试 + %(prog)s --no-build # 只运行测试,不重新构建 + %(prog)s --verbose # 详细输出 + """ + ) + + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-build", action="store_true", + help="不重新构建,直接运行测试") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + + args = parser.parse_args() + + # 开始测试 + start_time = time.time() + + self.print_header("Step 2: Configuration Physics Update") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 设置Intel环境 + self.print_step(1, 4, "Setting up Intel oneAPI environment") + if not self.setup_intel_environment(args): + if not args.force: + self.print_error("Intel environment setup failed") + return 1 + self.print_warning("Intel environment setup failed, continuing...") + + # 2. 构建项目(如果需要) + self.print_step(2, 4, "Building project if needed") + if not self.build_project(args): + if not args.force: + return 1 + + # 3. 查找测试可执行文件 + self.print_step(3, 4, "Finding test executable") + if not self.find_test_executable(): + self.print_error(f"Test executable {self.test_name}.exe not found") + if not args.force: + return 1 + self.print_warning("Test executable not found, but continuing due to --force") + return 0 + + # 4. 运行测试 + self.print_step(4, 4, "Running configuration physics test") + test_passed = self.run_test_with_intel_env() + + # 生成报告 + test_time = time.time() - start_time + self.print_header("Step 2 Complete") + + print(f"测试: {'通过 ✓' if test_passed else '失败 ✗'}") + print(f"测试程序: {self.test_exe.name if self.test_exe else '未找到'}") + print(f"总耗时: {test_time:.1f}秒") + + if test_passed: + print(f"\n下一步: 更新组件管理器以支持物理模块") + print(f"建议: 修改component_manager.f90,添加physics组件创建") + return 0 + else: + if args.force: + self.print_warning("测试失败,但由于--flag继续执行") + return 0 + return 1 + + except KeyboardInterrupt: + self.print_error("\n测试被用户中断") + return 1 + except Exception as e: + self.print_error(f"测试过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + system = Step2System() + return system.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03/src/CMakeLists.txt new file mode 100644 index 00000000..2aaee245 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/CMakeLists.txt @@ -0,0 +1,18 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(base) +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) +add_subdirectory(physics) # ← 新增物理模块目录 +add_subdirectory(manager) +add_subdirectory(solver) + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/base/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03/src/base/CMakeLists.txt new file mode 100644 index 00000000..74f4aa65 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/base/CMakeLists.txt @@ -0,0 +1,16 @@ +# src/base/CMakeLists.txt +message(STATUS "Configuring base module...") + +add_library(base STATIC + modules.f90 + precision.f90 # 新增 +) + +set_target_properties(base PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Base module configured") + +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/base/modules.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/src/base/modules.f90 new file mode 100644 index 00000000..43aaee24 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/base/modules.f90 @@ -0,0 +1,36 @@ +! src/base/modules.f90 +module base_modules + use, intrinsic :: iso_fortran_env, only: real64, int32 + implicit none + + public :: wp, ip, max_name_len, string_len, cfd_config_base, component_info + + integer, parameter :: wp = real64 + integer, parameter :: ip = int32 + integer, parameter :: string_len = 100 + integer, parameter :: max_name_len = 32 + + ! 基础配置类型 + type :: cfd_config_base + character(len=max_name_len) :: ic_type = "step" + character(len=max_name_len) :: recon_scheme = "eno" + character(len=max_name_len) :: flux_type = "rusanov" + integer(ip) :: rk_order = 1 + real(wp) :: wave_speed = 1.0_wp + real(wp) :: final_time = 0.625_wp + real(wp) :: dt = 0.025_wp + character(len=max_name_len) :: boundary_type = "periodic" + integer(ip) :: spatial_order = 2 + character(len=max_name_len) :: equation_type = "linear_advection" + character(len=max_name_len) :: problem_type = "linear_advection" + logical :: verbose = .true. + end type cfd_config_base + + ! 组件信息类型 + type :: component_info + character(len=max_name_len) :: category = "" + character(len=max_name_len) :: name = "" + integer(ip) :: order = 0 + end type component_info + +end module base_modules \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/base/precision.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/src/base/precision.f90 new file mode 100644 index 00000000..4ac5fd7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/base/precision.f90 @@ -0,0 +1,9 @@ +! src/base/precision.f90(简单版本) +module precision_module + use base_modules, only: wp, ip + implicit none + + ! 重新导出,确保兼容 + public :: wp, ip + +end module precision_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03/src/core/CMakeLists.txt new file mode 100644 index 00000000..d8b8df06 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/core/CMakeLists.txt @@ -0,0 +1,14 @@ +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") + +add_library(core STATIC + registry.f90 +) + +target_link_libraries(core PRIVATE base) + +set_target_properties(core PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Core module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/core/factory_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/src/core/factory_base.f90 new file mode 100644 index 00000000..302418a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/core/factory_base.f90 @@ -0,0 +1,57 @@ +! src/core/factory_base.f90 +module factory_base_module + use base_modules, only: wp, ip + use registry_module, only: create_component, has_component + + implicit none + private + public :: wp, ip, factory_base, factory_create + + ! 工厂基类 + type :: factory_base + character(len=max_name_length) :: category = "" + contains + procedure :: create => factory_base_create + procedure :: get_available => factory_base_get_available + end type factory_base + + ! 便捷函数类型 + abstract interface + function factory_function_interface(category, name) result(instance) + import :: wp + character(len=*), intent(in) :: category, name + class(*), allocatable :: instance + end function factory_function_interface + end interface + +contains + + ! 创建工厂实例 + function factory_create(category) result(factory) + character(len=*), intent(in) :: category + type(factory_base) :: factory + factory%category = trim(category) + end function factory_create + + ! 工厂创建方法 + function factory_base_create(this, name) result(instance) + class(factory_base), intent(in) :: this + character(len=*), intent(in) :: name + class(*), allocatable :: instance + + instance = create_component(this%category, name) + end function factory_base_create + + ! 获取可用组件列表(简化版) + subroutine factory_base_get_available(this, names, count) + class(factory_base), intent(in) :: this + character(len=*), allocatable, intent(out) :: names(:) + integer(ip), intent(out) :: count + + ! 这里需要实现从注册表获取列表的逻辑 + ! 暂时返回空列表 + count = 0 + allocate(character(len=max_name_length) :: names(0)) + end subroutine factory_base_get_available + +end module factory_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..a960167c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/src/core/registry.f90 new file mode 100644 index 00000000..acc63edb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/core/registry.f90 @@ -0,0 +1,203 @@ +! src/core/registry.f90 +module registry_module + use base_modules, only: wp, ip, max_name_len, component_info + + implicit none + private + + ! 明确公开所有需要的接口 + public :: wp, ip ! 类型参数 + public :: component_info ! 类型 + public :: registry_init, registry_cleanup ! 初始化/清理 + public :: register_component_simple ! 注册组件 + public :: has_component_simple ! 检查组件 + public :: list_components ! 列出组件 + public :: registry_is_initialized ! 检查初始化状态 ← 新增 + public :: registry_get_size ! 获取大小 ← 新增 + + ! 全局注册表 + type :: component_registry + type(component_info), allocatable :: components(:) + integer(ip) :: count = 0 + integer(ip) :: capacity = 100 + logical :: initialized = .false. + logical :: verbose = .true. + end type component_registry + + type(component_registry) :: registry + +contains + + ! ==================== 公共API ==================== + + subroutine registry_init(verbose) + logical, optional, intent(in) :: verbose + + if (registry%initialized) then + if (registry%verbose) then + print *, "[REGISTRY] Already initialized" + end if + return + end if + + if (present(verbose)) then + registry%verbose = verbose + end if + + allocate(registry%components(registry%capacity)) + registry%initialized = .true. + + if (registry%verbose) then + print *, "[REGISTRY] Initialized with capacity:", registry%capacity + end if + end subroutine registry_init + + subroutine registry_cleanup() + if (allocated(registry%components)) then + deallocate(registry%components) + end if + registry%initialized = .false. + registry%count = 0 + + if (registry%verbose) then + print *, "[REGISTRY] Cleaned up" + end if + end subroutine registry_cleanup + + subroutine register_component_simple(category, name, order) + character(len=*), intent(in) :: category, name + integer(ip), optional, intent(in) :: order + + integer(ip) :: i + type(component_info) :: info + + if (.not. registry%initialized) then + call registry_init() + end if + + ! 检查是否已存在 + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + if (registry%verbose) then + print *, "[WARN] Overwriting component: ", trim(category), ".", trim(name) + end if + + ! 更新 + if (present(order)) then + registry%components(i)%order = order + else + registry%components(i)%order = 0 + end if + return + end if + end do + + ! 扩展数组 + if (registry%count >= registry%capacity) then + call expand_registry() + end if + + ! 添加新组件 + registry%count = registry%count + 1 + + info%category = trim(category) + info%name = trim(name) + info%order = 0 + if (present(order)) then + info%order = order + end if + + registry%components(registry%count) = info + + if (registry%verbose) then + print *, "[OK] Registered simple: ", trim(category), ".", trim(name) + end if + end subroutine register_component_simple + + logical function has_component_simple(category, name) + character(len=*), intent(in) :: category, name + + integer(ip) :: i + + has_component_simple = .false. + + if (.not. registry%initialized) return + + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + has_component_simple = .true. + return + end if + end do + end function has_component_simple + + subroutine list_components(category) + character(len=*), optional, intent(in) :: category + + integer(ip) :: i, count + + if (.not. registry%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + if (registry%count == 0) then + print *, "[INFO] No components registered" + return + end if + + count = 0 + print *, "=== Registry Contents ===" + do i = 1, registry%count + if (.not. present(category) .or. & + trim(registry%components(i)%category) == trim(category)) then + call print_component_info(registry%components(i)) + count = count + 1 + end if + end do + + print *, "Total:", count, "components" + print *, "==========================" + end subroutine list_components + + ! ==================== 新增函数 ==================== + + logical function registry_is_initialized() + ! 检查注册表是否已初始化 + registry_is_initialized = registry%initialized + end function registry_is_initialized + + integer(ip) function registry_get_size() + ! 获取注册表中的组件数量 + registry_get_size = registry%count + end function registry_get_size + + ! ==================== 内部辅助函数 ==================== + + subroutine expand_registry() + type(component_info), allocatable :: temp(:) + + registry%capacity = registry%capacity * 2 + allocate(temp(registry%capacity)) + temp(1:registry%count) = registry%components(1:registry%count) + call move_alloc(temp, registry%components) + + if (registry%verbose) then + print *, "[INFO] Registry expanded to capacity:", registry%capacity + end if + end subroutine expand_registry + + subroutine print_component_info(info) + type(component_info), intent(in) :: info + + if (info%order > 0) then + print *, " [", trim(info%category), ".", trim(info%name), & + " (order:", info%order, ")]" + else + print *, " [", trim(info%category), ".", trim(info%name), "]" + end if + end subroutine print_component_info + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..70cbbd2f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/infrastructure/CMakeLists.txt @@ -0,0 +1,17 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "Configuring infrastructure module...") + +add_library(infrastructure STATIC + config.f90 + mesh.f90 + domain.f90 # 新增 + solution.f90 # 新增 +) + +target_link_libraries(infrastructure PRIVATE base) + +set_target_properties(infrastructure PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Infrastructure module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/src/infrastructure/config.f90 new file mode 100644 index 00000000..7586a1a5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/infrastructure/config.f90 @@ -0,0 +1,144 @@ +! src/infrastructure/config.f90 (修复版) +module config_module + use base_modules, only: wp, ip, max_name_len, cfd_config_base + + implicit none + public :: wp, ip, cfd_config, config_print, config_with_reconstruction + + ! 扩展配置类型 - 添加物理相关字段 + type, extends(cfd_config_base) :: cfd_config + ! 物理参数 + real(wp) :: left_boundary_value = 1.0_wp + real(wp) :: right_boundary_value = 2.0_wp + real(wp) :: domain_length = 2.0_wp + + ! 新增:物理模块相关配置 + real(wp) :: pulse_center = 0.5_wp ! 高斯脉冲中心 + real(wp) :: pulse_width = 0.1_wp ! 高斯脉冲宽度 + logical :: enable_physics = .true. ! 是否启用物理模块 + contains + ! 新增:物理相关配置方法 + procedure :: set_physics_parameters + procedure :: get_physics_info + end type cfd_config + +contains + + subroutine config_print(cfg) + type(cfd_config), intent(in) :: cfg + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(cfg%ic_type) + print *, "Reconstruction: ", trim(cfg%recon_scheme), " (order:", cfg%spatial_order, ")" + print *, "Flux type: ", trim(cfg%flux_type) + print *, "Time integration: RK", cfg%rk_order + print *, "Wave speed: ", cfg%wave_speed + print *, "Final time: ", cfg%final_time + print *, "Time step: ", cfg%dt + print *, "Boundary: ", trim(cfg%boundary_type) + + ! 新增:物理配置信息 + print *, "--- Physics Configuration ---" + print *, "Equation type: ", trim(cfg%equation_type) + print *, "Problem type: ", trim(cfg%problem_type) + print *, "Domain length: ", cfg%domain_length + print *, "Physics enabled: ", cfg%enable_physics + + if (cfg%ic_type == "gaussian") then + print *, "Pulse center: ", cfg%pulse_center + print *, "Pulse width: ", cfg%pulse_width + end if + + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(cfg, scheme, order) + type(cfd_config), intent(inout) :: cfg + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + integer :: i + + ! 转换为小写 + cfg%recon_scheme = scheme + do i = 1, len_trim(cfg%recon_scheme) + if (cfg%recon_scheme(i:i) >= 'A' .and. cfg%recon_scheme(i:i) <= 'Z') then + cfg%recon_scheme(i:i) = char(ichar(cfg%recon_scheme(i:i)) + 32) + end if + end do + + ! 设置阶数 + if (present(order)) then + cfg%spatial_order = order + else + if (index(cfg%recon_scheme, 'weno') > 0) then + cfg%spatial_order = 5 + else if (trim(cfg%recon_scheme) == 'eno') then + cfg%spatial_order = 3 + end if + end if + + if (cfg%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(cfg%recon_scheme), & + " Order: ", cfg%spatial_order + end if + end subroutine config_with_reconstruction + + ! ========== 新增:物理参数设置方法 ========== + + subroutine set_physics_parameters(this, equation_type, problem_type, & + domain_length, enable_physics) + class(cfd_config), intent(inout) :: this + character(len=*), intent(in), optional :: equation_type, problem_type + real(wp), intent(in), optional :: domain_length + logical, intent(in), optional :: enable_physics + + if (present(equation_type)) then + this%equation_type = trim(equation_type) + if (this%verbose) then + print *, "[CONFIG] Set equation type: ", trim(this%equation_type) + end if + end if + + if (present(problem_type)) then + this%problem_type = trim(problem_type) + if (this%verbose) then + print *, "[CONFIG] Set problem type: ", trim(this%problem_type) + end if + end if + + if (present(domain_length)) then + this%domain_length = domain_length + if (this%verbose) then + print *, "[CONFIG] Set domain length: ", this%domain_length + end if + end if + + if (present(enable_physics)) then + this%enable_physics = enable_physics + if (this%verbose) then + print *, "[CONFIG] Physics module enabled: ", this%enable_physics + end if + end if + end subroutine set_physics_parameters + + subroutine get_physics_info(this) + class(cfd_config), intent(in) :: this + + print *, "=== Physics Configuration Info ===" + print *, "Equation type: ", trim(this%equation_type) + print *, "Problem type: ", trim(this%problem_type) + print *, "Domain length: ", this%domain_length + print *, "Wave speed: ", this%wave_speed + print *, "Physics enabled: ", this%enable_physics + + if (this%ic_type == "gaussian") then + print *, "Pulse parameters:" + print *, " Center: ", this%pulse_center + print *, " Width: ", this%pulse_width + end if + + print *, "==================================" + end subroutine get_physics_info + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/infrastructure/domain.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/src/infrastructure/domain.f90 new file mode 100644 index 00000000..c3662f03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/infrastructure/domain.f90 @@ -0,0 +1,102 @@ +! src/infrastructure/domain.f90 +module domain_module + use base_modules, only: wp, ip, max_name_len + use config_module, only: cfd_config + use mesh_module, only: mesh_type + + implicit none + private + public :: wp, ip, domain_type, domain_create, is_physical_cell + + type :: domain_type + type(cfd_config), pointer :: config => null() + type(mesh_type), pointer :: mesh => null() + integer(ip) :: nghosts = 0 + integer(ip) :: ist = 1 ! 物理区域起始索引(1-based) + integer(ip) :: ied = 1 ! 物理区域结束索引(exclusive) + integer(ip) :: ntcells = 0 ! 总单元数(含ghost) + contains + procedure :: print_info => domain_print_info + procedure :: get_physical_indices => domain_get_physical_indices + end type domain_type + +contains + + function domain_create(config, mesh) result(domain) + type(cfd_config), target, intent(in) :: config + type(mesh_type), target, intent(in) :: mesh + type(domain_type) :: domain + + domain%config => config + domain%mesh => mesh + + ! 计算ghost层数(参考Julia的_calc_nghosts) + domain%nghosts = calc_nghosts(config) + domain%ist = domain%nghosts + 1 + domain%ied = domain%ist + mesh%ncells + domain%ntcells = mesh%ncells + 2 * domain%nghosts + + if (config%verbose) then + print *, "[DOMAIN] Created:" + print *, " Ghost layers: ", domain%nghosts + print *, " Physical cells: ", domain%ist, " to ", domain%ied - 1 + print *, " Total cells: ", domain%ntcells + end if + end function domain_create + + function calc_nghosts(config) result(nghosts) + type(cfd_config), intent(in) :: config + integer(ip) :: nghosts + + character(len=max_name_len) :: scheme + + scheme = config%recon_scheme + + if (scheme == "eno") then + nghosts = config%spatial_order + else if (index(scheme, "weno") > 0) then + nghosts = config%spatial_order / 2 + 1 + else + print *, "[WARNING] Unknown scheme, using default nghosts=2" + nghosts = 2 + end if + + if (nghosts <= 0) then + print *, "[ERROR] Invalid nghosts: ", nghosts + nghosts = 2 + end if + end function calc_nghosts + + logical function is_physical_cell(this, idx) + class(domain_type), intent(in) :: this + integer(ip), intent(in) :: idx + is_physical_cell = (idx >= this%ist .and. idx < this%ied) + end function is_physical_cell + + function domain_get_physical_indices(this) result(indices) + class(domain_type), intent(in) :: this + integer(ip), allocatable :: indices(:) + integer(ip) :: i, count + + count = this%ied - this%ist + allocate(indices(count)) + + do i = 1, count + indices(i) = this%ist + i - 1 + end do + end function domain_get_physical_indices + + subroutine domain_print_info(this) + class(domain_type), intent(in) :: this + + print *, "=== Domain Information ===" + print *, "Configuration: ", trim(this%config%recon_scheme), & + " order ", this%config%spatial_order + print *, "Ghost layers: ", this%nghosts + print *, "Physical cells: ", this%ist, " to ", this%ied - 1 + print *, "Total cells: ", this%ntcells + print *, "Mesh cells: ", this%mesh%ncells + print *, "==========================" + end subroutine domain_print_info + +end module domain_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..f810f3a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/infrastructure/mesh.f90 @@ -0,0 +1,73 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use base_modules, only: wp, ip + + implicit none + public :: wp, ip, mesh_type, mesh_init, mesh_print_info + + ! 网格类型 + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer(ip) :: ncells = 40 + integer(ip) :: nnodes + integer(ip) :: nx + real(wp), allocatable :: x(:) ! 节点坐标 + real(wp), allocatable :: xcc(:) ! 单元中心坐标 + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer(ip), optional, intent(in) :: ncells + + integer(ip) :: i + + ! 设置参数 + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! 计算 + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! 分配内存 + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! 生成节点坐标 + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! 生成单元中心坐标 + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== Mesh Information ===" + print *, "Domain: [", this%xmin, ", ", this%xmax, "]" + print *, "Cells: ", this%ncells + print *, "Nodes: ", this%nnodes + print *, "dx: ", this%dx + print *, "L: ", this%L + print *, "========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/infrastructure/solution.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/src/infrastructure/solution.f90 new file mode 100644 index 00000000..ce88fd8a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/infrastructure/solution.f90 @@ -0,0 +1,131 @@ +! src/infrastructure/solution.f90 +module solution_module + use base_modules, only: wp, ip + use domain_module, only: domain_type + + implicit none + private + public :: wp, ip, solution_type, solution_create, solution_reset + + type :: solution_type + type(domain_type), pointer :: domain => null() + real(wp), allocatable :: u(:) ! 当前解(含ghost) + real(wp), allocatable :: un(:) ! 旧解 + real(wp), allocatable :: q_face_left(:) ! 左界面值 + real(wp), allocatable :: q_face_right(:)! 右界面值 + real(wp), allocatable :: flux(:) ! 通量 + real(wp), allocatable :: res(:) ! 残差 + contains + procedure :: initialize => solution_initialize + procedure :: update_old_field => solution_update_old_field + procedure :: print_info => solution_print_info + procedure :: reset => solution_reset_instance + end type solution_type + +contains + + function solution_create(domain) result(solution) + type(domain_type), target, intent(in) :: domain + type(solution_type) :: solution + + integer(ip) :: ncells, nnodes, ntcells + + solution%domain => domain + + ncells = domain%mesh%ncells + nnodes = domain%mesh%nnodes + ntcells = domain%ntcells + + ! 分配数组(与Julia solution.jl一致) + allocate(solution%u(ntcells), source=0.0_wp) + allocate(solution%un(ntcells), source=0.0_wp) + allocate(solution%q_face_left(nnodes), source=0.0_wp) + allocate(solution%q_face_right(nnodes), source=0.0_wp) + allocate(solution%flux(nnodes), source=0.0_wp) + allocate(solution%res(ncells), source=0.0_wp) + + if (domain%config%verbose) then + print *, "[SOLUTION] Created:" + print *, " u size: ", size(solution%u), " (with ghosts)" + print *, " flux size: ", size(solution%flux) + print *, " res size: ", size(solution%res) + end if + end function solution_create + + subroutine solution_initialize(this, initial_values) + class(solution_type), intent(inout) :: this + real(wp), intent(in), optional :: initial_values(:) + + integer(ip) :: i, idx + type(domain_type), pointer :: domain + + domain => this%domain + + if (present(initial_values)) then + ! 应用初始值到物理区域 + do i = domain%ist, domain%ied - 1 + idx = i - domain%ist + 1 + if (idx <= size(initial_values)) then + this%u(i) = initial_values(idx) + end if + end do + else + ! 默认为0 + this%u = 0.0_wp + end if + + ! 同步旧场(与Julia的update_old_field一致) + call this%update_old_field() + + if (domain%config%verbose) then + print *, "[SOLUTION] Initialized" + print *, " u range: ", minval(this%u), " to ", maxval(this%u) + end if + end subroutine solution_initialize + + subroutine solution_update_old_field(this) + class(solution_type), intent(inout) :: this + this%un = this%u ! 与Julia的 un .= u 一致 + end subroutine solution_update_old_field + + subroutine solution_reset_instance(this) + class(solution_type), intent(inout) :: this + call solution_reset(this) + end subroutine solution_reset_instance + + subroutine solution_reset(solution) + type(solution_type), intent(inout) :: solution + + if (allocated(solution%u)) solution%u = 0.0_wp + if (allocated(solution%un)) solution%un = 0.0_wp + if (allocated(solution%q_face_left)) solution%q_face_left = 0.0_wp + if (allocated(solution%q_face_right)) solution%q_face_right = 0.0_wp + if (allocated(solution%flux)) solution%flux = 0.0_wp + if (allocated(solution%res)) solution%res = 0.0_wp + + if (associated(solution%domain) .and. solution%domain%config%verbose) then + print *, "[SOLUTION] Reset" + end if + end subroutine solution_reset + + subroutine solution_print_info(this) + class(solution_type), intent(in) :: this + + print *, "=== Solution Information ===" + print *, "Arrays:" + print *, " u: ", size(this%u), " elements" + print *, " un: ", size(this%un), " elements" + print *, " q_face_left: ", size(this%q_face_left), " elements" + print *, " q_face_right: ", size(this%q_face_right), " elements" + print *, " flux: ", size(this%flux), " elements" + print *, " res: ", size(this%res), " elements" + + if (allocated(this%u)) then + print *, "Values:" + print *, " u min/max: ", minval(this%u), maxval(this%u) + print *, " un min/max: ", minval(this%un), maxval(this%un) + end if + print *, "============================" + end subroutine solution_print_info + +end module solution_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/manager/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03/src/manager/CMakeLists.txt new file mode 100644 index 00000000..00c8bf49 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/manager/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/manager/CMakeLists.txt +message(STATUS "配置管理器模块...") + +# 创建管理器库 +add_library(manager STATIC + component_manager.f90 + component_factory.f90 +) + +# 明确依赖关系:管理器依赖所有其他模块 +target_link_libraries(manager + PRIVATE + core + infrastructure + reconstructor + flux +) + +# 设置模块输出目录 +set_target_properties(manager PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "管理器模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/manager/component_factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/src/manager/component_factory.f90 new file mode 100644 index 00000000..114fedea --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/manager/component_factory.f90 @@ -0,0 +1,127 @@ +! src/manager/component_factory.f90 +module component_factory_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + use eno_reconstructor_module, only: eno_reconstructor, create_eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor, create_weno3_reconstructor + use rusanov_flux_module, only: rusanov_flux, create_rusanov_flux + + implicit none + private + public :: wp, create_reconstructor, create_flux_calculator + + ! 错误代码 + integer, parameter :: CM_SUCCESS = 0 + integer, parameter :: CM_ERROR_UNKNOWN_SCHEME = 1 + integer, parameter :: CM_ERROR_UNKNOWN_FLUX = 2 + integer, parameter :: CM_ERROR_INVALID_ORDER = 3 + +contains + + ! ==================== 重构器创建 ==================== + + function create_reconstructor(config, status) result(recon) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(reconstructor_base), allocatable :: recon + + character(len=20) :: scheme + integer :: order, error_code + + scheme = trim(adjustl(config%recon_scheme)) + order = config%spatial_order + + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating reconstructor: ", scheme, " order=", order + end if + + select case(scheme) + case('eno') + allocate(eno_reconstructor :: recon) + select type(recon) + type is(eno_reconstructor) + recon = create_eno_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case('weno3') + allocate(weno3_reconstructor :: recon) + select type(recon) + type is(weno3_reconstructor) + recon = create_weno3_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case default + error_code = CM_ERROR_UNKNOWN_SCHEME + if (config%verbose) then + print *, "[ERROR] Unknown reconstructor scheme: ", scheme + print *, " Available: eno, weno3" + end if + end select + + ! 检查阶数有效性 + if (error_code == CM_SUCCESS) then + if (order < 1) then + error_code = CM_ERROR_INVALID_ORDER + if (config%verbose) then + print *, "[ERROR] Invalid spatial order: ", order + end if + end if + end if + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Reconstructor creation failed" + end if + end function create_reconstructor + + ! ==================== 通量计算器创建 ==================== + + function create_flux_calculator(config, status) result(flux) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(flux_calculator_base), allocatable :: flux + + character(len=20) :: flux_type + integer :: error_code + + flux_type = trim(adjustl(config%flux_type)) + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating flux calculator: ", flux_type + end if + + select case(flux_type) + case('rusanov') + allocate(rusanov_flux :: flux) + select type(flux) + type is(rusanov_flux) + flux = create_rusanov_flux() + flux%wave_speed_default = config%wave_speed + end select + + case default + error_code = CM_ERROR_UNKNOWN_FLUX + if (config%verbose) then + print *, "[ERROR] Unknown flux type: ", flux_type + print *, " Available: rusanov" + end if + end select + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Flux calculator creation failed" + end if + end function create_flux_calculator + +end module component_factory_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/manager/component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/src/manager/component_manager.f90 new file mode 100644 index 00000000..9e095c25 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/manager/component_manager.f90 @@ -0,0 +1,75 @@ +! src/manager/component_manager.f90 +module component_manager_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use component_factory_module, only: create_reconstructor, create_flux_calculator + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + implicit none + private + public :: wp, component_manager_info, validate_config + public :: create_reconstructor, create_flux_calculator + +contains + + ! ==================== 配置验证 ==================== + + function validate_config(config) result(is_valid) + type(cfd_config), intent(in) :: config + logical :: is_valid + + integer :: status + class(reconstructor_base), allocatable :: test_recon + class(flux_calculator_base), allocatable :: test_flux + + is_valid = .false. + + ! 测试创建重构器 + test_recon = create_reconstructor(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid reconstructor configuration" + end if + return + end if + + ! 测试创建通量计算器 + test_flux = create_flux_calculator(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid flux configuration" + end if + return + end if + + ! 清理测试组件 + if (allocated(test_recon)) deallocate(test_recon) + if (allocated(test_flux)) deallocate(test_flux) + + is_valid = .true. + + if (config%verbose) then + print *, "[CONFIG VALIDATION] Configuration is valid" + end if + end function validate_config + + ! ==================== 信息显示 ==================== + + subroutine component_manager_info() + print *, "=== Component Manager ===" + print *, "Available reconstructors:" + print *, " - eno (orders: 1-7)" + print *, " - weno3 (order: 3)" + print *, "" + print *, "Available flux calculators:" + print *, " - rusanov" + print *, "" + print *, "Features:" + print *, " - Configuration validation" + print *, " - Component creation from config" + print *, " - Error handling with status codes" + print *, "=========================" + end subroutine component_manager_info + +end module component_manager_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..3fde7746 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,28 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux STATIC + base.f90 + rusanov.f90 +) + +#target_link_libraries(flux PRIVATE core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 明确设置不使用 /Qm64 选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + set_target_properties(flux PROPERTIES + Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /Qm64" # 明确设置,避免继承问题 + ) +endif() + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/src/numerics/flux/base.f90 new file mode 100644 index 00000000..7080a7ae --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/numerics/flux/base.f90 @@ -0,0 +1,30 @@ +!src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + procedure :: print_basic_info => flux_print_basic ! 添加辅助方法 + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + + subroutine flux_print_basic(this) + class(flux_calculator_base), intent(in) :: this + print *, " Name: ", trim(this%name) + end subroutine flux_print_basic + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..daa9e3bb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/numerics/flux/rusanov.f90 @@ -0,0 +1,41 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux, create_rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + + ! 构造函数接口 + interface rusanov_flux + module procedure create_rusanov_flux + end interface + +contains + + ! 构造函数 + type(rusanov_flux) function create_rusanov_flux() result(this) + this%name = "Rusanov" + this%wave_speed_default = 1.0_real64 + end function create_rusanov_flux + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Flux calculator information:" + call this%print_basic_info() + + ! 添加Rusanov特有信息 + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..5e4b938d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,22 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor STATIC + base.f90 + eno.f90 + weno3.f90 +) + +target_link_libraries(reconstructor PRIVATE core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..53798d02 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/numerics/reconstructor/base.f90 @@ -0,0 +1,33 @@ +!src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + procedure :: print_basic_info => reconstructor_print_basic ! 添加一个辅助方法 + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + + subroutine reconstructor_print_basic(this) + class(reconstructor_base), intent(in) :: this + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_print_basic + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..f973e8b3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor, create_eno_reconstructor ! ← 添加这个 + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + + ! 构造函数接口 + interface eno_reconstructor + module procedure create_eno_reconstructor + end interface + +contains + + ! 构造函数 + type(eno_reconstructor) function create_eno_reconstructor() result(this) + this%name = "ENO" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_eno_reconstructor + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加ENO特有信息 + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..d5b7a747 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno3_reconstructor, create_weno3_reconstructor + + type, extends(reconstructor_base) :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + + ! 构造函数接口 + interface weno3_reconstructor + module procedure create_weno3_reconstructor + end interface + +contains + + ! 构造函数 + type(weno3_reconstructor) function create_weno3_reconstructor() result(this) + this%name = "WENO3" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_weno3_reconstructor + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO3特有信息 + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/physics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03/src/physics/CMakeLists.txt new file mode 100644 index 00000000..cc4e233a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/physics/CMakeLists.txt @@ -0,0 +1,19 @@ +# src/physics/CMakeLists.txt +message(STATUS "配置物理模块...") + +# 创建物理模块库 +add_library(physics STATIC + physics_interface.f90 + equations/linear_convection.f90 + problems/linear_convection_problem.f90 +) + +# 链接依赖 +target_link_libraries(physics PRIVATE base) + +# 设置模块输出目录 +set_target_properties(physics PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "物理模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/physics/equations/linear_convection.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/src/physics/equations/linear_convection.f90 new file mode 100644 index 00000000..595a2110 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/physics/equations/linear_convection.f90 @@ -0,0 +1,83 @@ +! src/physics/equations/linear_convection.f90 +module linear_convection_equation + use precision_module, only: wp, ip + use physics_interface, only: physics_equation + implicit none + private + + public :: linear_convection_eq, create_linear_convection_eq + public :: linear_convection_flux, linear_convection_speed + + ! 具体方程类型 + type, extends(physics_equation) :: linear_convection_eq + real(wp) :: wave_speed = 1.0_wp + contains + procedure :: flux => lc_flux + procedure :: speed => lc_speed + end type linear_convection_eq + + ! 独立函数(供外部调用) + interface linear_convection_flux + module procedure :: lc_flux_func + end interface + + interface linear_convection_speed + module procedure :: lc_speed_func + end interface + +contains + + ! 构造函数 + function create_linear_convection_eq(wave_speed) result(eq) + real(wp), intent(in), optional :: wave_speed + type(linear_convection_eq) :: eq + + eq%name = "Linear Convection" + if (present(wave_speed)) then + eq%wave_speed = wave_speed + else + eq%wave_speed = 1.0_wp + end if + end function create_linear_convection_eq + + ! 方法实现 + pure function lc_flux(this, u) result(f) + class(linear_convection_eq), intent(in) :: this + real(wp), intent(in) :: u + real(wp) :: f + f = this%wave_speed * u + end function lc_flux + + pure function lc_speed(this) result(a) + class(linear_convection_eq), intent(in) :: this + real(wp) :: a + a = this%wave_speed + end function lc_speed + + ! 独立函数 + pure function lc_flux_func(u, wave_speed) result(f) + real(wp), intent(in) :: u + real(wp), intent(in), optional :: wave_speed + real(wp) :: f + real(wp) :: a + + if (present(wave_speed)) then + a = wave_speed + else + a = 1.0_wp + end if + f = a * u + end function lc_flux_func + + pure function lc_speed_func(wave_speed) result(a) + real(wp), intent(in), optional :: wave_speed + real(wp) :: a + + if (present(wave_speed)) then + a = wave_speed + else + a = 1.0_wp + end if + end function lc_speed_func + +end module linear_convection_equation \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/physics/physics_interface.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/src/physics/physics_interface.f90 new file mode 100644 index 00000000..55662e5c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/physics/physics_interface.f90 @@ -0,0 +1,64 @@ +! src/physics/physics_interface.f90 +module physics_interface + use precision_module, only: wp, ip + implicit none + private + + ! 定义抽象基类型 + type, abstract :: physics_equation + character(len=:), allocatable :: name + contains + procedure(eq_flux_abs), deferred :: flux + procedure(eq_speed_abs), deferred :: speed + end type physics_equation + + type, abstract :: physics_problem + character(len=:), allocatable :: name + contains + procedure(prob_ic_abs), deferred :: initial_condition + procedure(prob_bc_abs), deferred :: boundary_condition + procedure(prob_exact_abs), deferred :: exact_solution + end type physics_problem + + ! 抽象接口定义 + abstract interface + pure function eq_flux_abs(this, u) result(f) + import :: physics_equation, wp + class(physics_equation), intent(in) :: this + real(wp), intent(in) :: u + real(wp) :: f + end function eq_flux_abs + + pure function eq_speed_abs(this) result(a) + import :: physics_equation, wp + class(physics_equation), intent(in) :: this + real(wp) :: a + end function eq_speed_abs + + subroutine prob_ic_abs(this, x, u) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp), intent(out) :: u(:) + end subroutine prob_ic_abs + + subroutine prob_bc_abs(this, u, t) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(inout) :: u(:) + real(wp), intent(in), optional :: t + end subroutine prob_bc_abs + + function prob_exact_abs(this, x, t) result(u) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(in) :: x(:), t + real(wp), dimension(size(x)) :: u + end function prob_exact_abs + end interface + + ! 公开接口 + public :: physics_equation, physics_problem + public :: wp, ip + +end module physics_interface \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/physics/problems/linear_convection_problem.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/src/physics/problems/linear_convection_problem.f90 new file mode 100644 index 00000000..31210710 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/physics/problems/linear_convection_problem.f90 @@ -0,0 +1,116 @@ +! src/physics/problems/linear_convection_problem.f90 +module linear_convection_problem + use precision_module, only: wp, ip + use physics_interface, only: physics_problem + implicit none + private + + public :: linear_convection_prob, create_linear_convection_prob + + ! 具体问题类型 + type, extends(physics_problem) :: linear_convection_prob + real(wp) :: wave_speed = 1.0_wp + real(wp) :: domain_length = 2.0_wp + character(len=20) :: ic_type = "step" + character(len=20) :: boundary_type = "periodic" + contains + procedure :: initial_condition => lc_initial_condition + procedure :: boundary_condition => lc_boundary_condition + procedure :: exact_solution => lc_exact_solution + end type linear_convection_prob + +contains + + ! 构造函数 + function create_linear_convection_prob(wave_speed, domain_length, & + ic_type, boundary_type) result(prob) + real(wp), intent(in), optional :: wave_speed, domain_length + character(len=*), intent(in), optional :: ic_type, boundary_type + type(linear_convection_prob) :: prob + + prob%name = "Linear Convection Problem" + + if (present(wave_speed)) prob%wave_speed = wave_speed + if (present(domain_length)) prob%domain_length = domain_length + if (present(ic_type)) prob%ic_type = ic_type + if (present(boundary_type)) prob%boundary_type = boundary_type + end function create_linear_convection_prob + + ! 初始条件 + subroutine lc_initial_condition(this, x, u) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp), intent(out) :: u(:) + + integer :: i + + select case (trim(this%ic_type)) + case ("step") + do i = 1, size(x) + if (x(i) >= 0.5_wp .and. x(i) <= 1.0_wp) then + u(i) = 2.0_wp + else + u(i) = 1.0_wp + end if + end do + + case ("sin", "sine") + do i = 1, size(x) + u(i) = sin(2.0_wp * 3.141592653589793_wp * x(i) / this%domain_length) + end do + + case ("gaussian") + do i = 1, size(x) + u(i) = exp(-((x(i) - 0.5_wp) / 0.1_wp)**2) + end do + + case default + ! 默认阶跃函数 + do i = 1, size(x) + if (x(i) >= 0.5_wp .and. x(i) <= 1.0_wp) then + u(i) = 2.0_wp + else + u(i) = 1.0_wp + end if + end do + end select + end subroutine lc_initial_condition + + ! 边界条件(虚拟实现,实际在boundary模块) + subroutine lc_boundary_condition(this, u, t) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(inout) :: u(:) + real(wp), intent(in), optional :: t + + ! 边界条件将在独立模块实现 + print *, "[PROBLEM] Boundary condition placeholder" + if (present(t)) then + print *, " Time = ", t + end if + end subroutine lc_boundary_condition + + ! 精确解(周期性平移) + function lc_exact_solution(this, x, t) result(u) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(in) :: x(:), t + real(wp), dimension(size(x)) :: u + real(wp), dimension(size(x)) :: x_shifted + integer :: i + + ! 周期性平移 + do i = 1, size(x) + x_shifted(i) = x(i) - this%wave_speed * t + ! 确保在 [0, domain_length) 范围内 + do while (x_shifted(i) < 0.0_wp) + x_shifted(i) = x_shifted(i) + this%domain_length + end do + do while (x_shifted(i) >= this%domain_length) + x_shifted(i) = x_shifted(i) - this%domain_length + end do + end do + + ! 重用初始条件函数 + call this%initial_condition(x_shifted, u) + end function lc_exact_solution + +end module linear_convection_problem \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/solver/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03/src/solver/CMakeLists.txt new file mode 100644 index 00000000..3f320bca --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/solver/CMakeLists.txt @@ -0,0 +1,18 @@ +# src/solver/CMakeLists.txt +message(STATUS "配置求解器模块...") + +add_library(solver STATIC + base.f90 +) + +target_link_libraries(solver + PRIVATE + infrastructure + core +) + +set_target_properties(solver PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "求解器模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/src/solver/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/src/solver/base.f90 new file mode 100644 index 00000000..1881ba08 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/src/solver/base.f90 @@ -0,0 +1,258 @@ +! src/solver/base.f90 +module solver_base_module + use base_modules, only: wp => wp, ip => ip ! 重命名以避免冲突 + + use config_module, only: cfd_config + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create + + implicit none + private + + ! 明确导出列表 + public :: wp, ip ! 类型参数 + public :: solver_base, create_solver_base ! 类型和构造函数 + public :: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, SOLVER_RUNNING + public :: SOLVER_COMPLETED, SOLVER_ERROR ! 状态常量 + + ! 求解器状态枚举 + integer, parameter :: SOLVER_UNINITIALIZED = 0 + integer, parameter :: SOLVER_INITIALIZED = 1 + integer, parameter :: SOLVER_RUNNING = 2 + integer, parameter :: SOLVER_COMPLETED = 3 + integer, parameter :: SOLVER_ERROR = 4 + + ! 求解器基类 + type :: solver_base + ! 基本组件 + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + + ! 状态管理 + integer :: state = SOLVER_UNINITIALIZED + character(len=100) :: error_message = "" + real(wp) :: current_time = 0.0_wp + integer(ip) :: current_step = 0 + + ! 时间控制 + real(wp) :: dt_original = 0.0_wp + contains + procedure :: initialize => solver_base_initialize + procedure :: step => solver_base_step + procedure :: run_to_time => solver_base_run_to_time + procedure :: cleanup => solver_base_cleanup + procedure :: get_state => solver_base_get_state + procedure :: get_error => solver_base_get_error + procedure :: print_info => solver_base_print_info + end type solver_base + + ! 构造函数接口 + interface solver_base + module procedure create_solver_base + end interface + +contains + + ! ==================== 构造函数 ==================== + + function create_solver_base(config, mesh) result(solver) + type(cfd_config), intent(in) :: config + type(mesh_type), intent(in) :: mesh + type(solver_base) :: solver + + solver%config = config + solver%mesh = mesh + + ! 创建域 + solver%domain = domain_create(config, mesh) + + ! 创建解 + solver%solution = solution_create(solver%domain) + + ! 保存原始时间步长 + solver%dt_original = config%dt + + if (config%verbose) then + print *, "[SOLVER] Base solver created" + print *, " Mesh cells: ", mesh%ncells + print *, " Domain total cells: ", solver%domain%ntcells + end if + end function create_solver_base + + ! ==================== 初始化 ==================== + + subroutine solver_base_initialize(this) + class(solver_base), intent(inout) :: this + + if (this%state == SOLVER_INITIALIZED) then + if (this%config%verbose) then + print *, "[SOLVER] Already initialized" + end if + return + end if + + ! 初始化解(通过配置) + ! 这里暂时简化,实际需要调用初始条件工厂 + print *, "[INFO] Base solver initialized (simplified)" + + ! 更新状态 + this%state = SOLVER_INITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + + if (this%config%verbose) then + print *, "[SOLVER] Initialized at t = ", this%current_time + end if + end subroutine solver_base_initialize + + ! ==================== 单步计算(虚方法) ==================== + + subroutine solver_base_step(this, dt) + class(solver_base), intent(inout) :: this + real(wp), intent(in) :: dt + + ! 基类中这只是虚方法,需要在子类中实现 + print *, "[INFO] Base solver step (virtual method)" + print *, " dt = ", dt + print *, " t = ", this%current_time + + ! 更新时间 + this%current_time = this%current_time + dt + this%current_step = this%current_step + 1 + + ! 简单模拟:只是更新状态 + if (this%config%verbose) then + print *, "[SOLVER] Step completed: t = ", this%current_time, & + ", step = ", this%current_step + end if + end subroutine solver_base_step + + ! ==================== 运行到指定时间 ==================== + + subroutine solver_base_run_to_time(this, final_time) + class(solver_base), intent(inout) :: this + real(wp), intent(in) :: final_time + + real(wp) :: dt, t_remaining + integer :: step_count + + if (this%state /= SOLVER_INITIALIZED) then + this%error_message = "Solver not initialized" + this%state = SOLVER_ERROR + return + end if + + this%state = SOLVER_RUNNING + step_count = 0 + + if (this%config%verbose) then + print *, "[SOLVER] Running from t = ", this%current_time, & + " to t = ", final_time + end if + + do while (this%current_time < final_time) + ! 计算时间步长 + dt = min(this%config%dt, final_time - this%current_time) + + ! 执行时间步 + call this%step(dt) + + step_count = step_count + 1 + + ! 每10步输出一次进度 + if (mod(step_count, 10) == 0 .and. this%config%verbose) then + print *, "[SOLVER] Progress: t = ", this%current_time, & + " / ", final_time + end if + end do + + ! 恢复原始时间步长 + this%config%dt = this%dt_original + + ! 更新状态 + this%state = SOLVER_COMPLETED + + if (this%config%verbose) then + print *, "[SOLVER] Run completed:" + print *, " Final time: ", this%current_time + print *, " Total steps: ", this%current_step + end if + end subroutine solver_base_run_to_time + + ! ==================== 清理 ==================== + + subroutine solver_base_cleanup(this) + class(solver_base), intent(inout) :: this + + ! 重置状态 + this%state = SOLVER_UNINITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + this%error_message = "" + + if (this%config%verbose) then + print *, "[SOLVER] Cleaned up" + end if + end subroutine solver_base_cleanup + + ! ==================== 状态查询 ==================== + + function solver_base_get_state(this) result(state) + class(solver_base), intent(in) :: this + integer :: state + state = this%state + end function solver_base_get_state + + function solver_base_get_error(this) result(error_msg) + class(solver_base), intent(in) :: this + character(len=100) :: error_msg + error_msg = trim(this%error_message) + end function solver_base_get_error + + ! ==================== 信息打印 ==================== + + subroutine solver_base_print_info(this) + class(solver_base), intent(in) :: this + + character(len=20) :: state_str + + ! 状态字符串 + select case (this%state) + case (SOLVER_UNINITIALIZED) + state_str = "Uninitialized" + case (SOLVER_INITIALIZED) + state_str = "Initialized" + case (SOLVER_RUNNING) + state_str = "Running" + case (SOLVER_COMPLETED) + state_str = "Completed" + case (SOLVER_ERROR) + state_str = "Error" + case default + state_str = "Unknown" + end select + + print *, "=== Solver Information ===" + print *, "State: ", trim(state_str) + print *, "Current time: ", this%current_time + print *, "Current step: ", this%current_step + print *, "Error message: '", trim(this%error_message), "'" + + ! 配置信息 + print *, "Configuration:" + print *, " Scheme: ", trim(this%config%recon_scheme) + print *, " Order: ", this%config%spatial_order + print *, " dt: ", this%config%dt + + ! 域信息 + print *, "Domain:" + print *, " Ghost layers: ", this%domain%nghosts + print *, " Physical cells: ", this%domain%ist, " to ", this%domain%ied - 1 + + print *, "=========================" + end subroutine solver_base_print_info + +end module solver_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03/tests/CMakeLists.txt new file mode 100644 index 00000000..8dd2f902 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/tests/CMakeLists.txt @@ -0,0 +1,74 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +# +#message(STATUS "CMAKE_Fortran_MODULE_DIRECTORY=${CMAKE_Fortran_MODULE_DIRECTORY}") +# +add_executable(test_minimal_simple test_minimal_simple.f90) +target_link_libraries(test_minimal_simple + PRIVATE + core # 必须链接core库 + infrastructure +) + + +add_executable(test_simple_link test_simple_link.f90) +target_link_libraries(test_simple_link + PRIVATE + reconstructor + flux +) + + +add_executable(test_factory_simple test_factory_simple.f90) +target_link_libraries(test_factory_simple + PRIVATE + core + infrastructure + reconstructor + flux +) + + +add_executable(test_component_manager test_component_manager.f90) + +target_link_libraries(test_component_manager + PRIVATE + manager # ← 链接到新的管理器库 + infrastructure +) +add_executable(test_basic_only test_basic_only.f90) +target_link_libraries(test_basic_only + PRIVATE + infrastructure + core +) + +add_executable(test_physics_minimal test_physics_minimal.f90) +target_link_libraries(test_physics_minimal + PRIVATE + physics + base +) + +add_executable(test_domain_solution test_domain_solution.f90) +target_link_libraries(test_domain_solution + PRIVATE + infrastructure + core +) + +add_executable(test_config_physics test_config_physics.f90) +target_link_libraries(test_config_physics + PRIVATE + infrastructure + core +) + +add_executable(test_solver_base test_solver_base.f90) +target_link_libraries(test_solver_base + PRIVATE + solver + infrastructure + core +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_basic_only.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_basic_only.f90 new file mode 100644 index 00000000..20901ddc --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_basic_only.f90 @@ -0,0 +1,56 @@ +! tests/test_basic_only.f90 +program test_basic_only + ! 只测试最基本的功能,不依赖复杂模块 + use config_module, only: cfd_config, config_print, wp + use mesh_module, only: mesh_type + use registry_module, only: registry_init, registry_cleanup, & + register_component_simple, list_components + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "=== BASIC TEST - Minimal Functionality ===" + print *, "" + + ! 测试1: 配置 + print *, "1. Testing configuration..." + print *, "----------------------------" + call config_print(config) + print *, "" + + ! 测试2: 网格 + print *, "2. Testing mesh..." + print *, "------------------" + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=5) + print *, "Mesh initialized:" + print *, " Cells: ", mesh%ncells + print *, " Nodes: ", mesh%nnodes + print *, " dx: ", mesh%dx + print *, "" + + ! 测试3: 注册系统 + print *, "3. Testing registry..." + print *, "----------------------" + + call registry_init() + + ! 注册组件(使用简化版本) + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! 列出组件 + call list_components() + print *, "" + + ! 清理 + call registry_cleanup() + + print *, "=== TEST PASSED ===" + print *, "✓ Configuration works" + print *, "✓ Mesh works" + print *, "✓ Registry works" + +end program test_basic_only \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_cfd_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_cfd_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_cfd_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_component_manager.f90 new file mode 100644 index 00000000..f60c3505 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_component_manager.f90 @@ -0,0 +1,111 @@ +! tests/test_component_manager.f90 +program test_component_manager + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use component_manager_module, only: create_reconstructor, create_flux_calculator + use component_manager_module, only: component_manager_info, validate_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + implicit none + + type(cfd_config) :: config + class(reconstructor_base), allocatable :: recon + class(flux_calculator_base), allocatable :: flux + integer :: status + logical :: is_valid + + print *, "=== Component Manager Test ===" + print *, "" + + ! 显示组件管理器信息 + call component_manager_info() + print *, "" + + ! 测试1: 基本配置 + print *, "1. Testing basic ENO3 + Rusanov configuration..." + print *, "-----------------------------------------------" + + config%verbose = .true. + call config_print(config) + + ! 配置ENO3重构 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + call config_print(config) + print *, "" + + ! 验证配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Configuration is valid" + else + print *, "[ERROR] Configuration is invalid" + end if + print *, "" + + ! 测试2: 创建组件 + print *, "2. Testing component creation..." + print *, "--------------------------------" + + ! 创建重构器(带状态检查) + recon = create_reconstructor(config, status) + if (status == 0) then + print *, "[OK] Reconstructor created successfully" + call recon%info() + else + print *, "[ERROR] Failed to create reconstructor, code:", status + end if + print *, "" + + ! 创建通量计算器 + flux = create_flux_calculator(config, status) + if (status == 0) then + print *, "[OK] Flux calculator created successfully" + call flux%info() + else + print *, "[ERROR] Failed to create flux calculator, code:", status + end if + print *, "" + + ! 测试3: WENO3重构测试 + print *, "3. Testing WENO3 configuration..." + print *, "---------------------------------" + + call config_with_reconstruction(config, "weno3", 3) + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] WENO3 configuration is valid" + + ! 创建WENO3重构器 + recon = create_reconstructor(config) + call recon%info() + else + print *, "[ERROR] WENO3 configuration is invalid" + end if + print *, "" + + ! 测试4: 错误配置测试 + print *, "4. Testing invalid configuration..." + print *, "-----------------------------------" + + config%recon_scheme = "unknown_scheme" + config%flux_type = "unknown_flux" + + is_valid = validate_config(config) + if (.not. is_valid) then + print *, "[OK] Invalid configuration correctly rejected" + else + print *, "[ERROR] Invalid configuration should have been rejected" + end if + + ! 清理 + if (allocated(recon)) deallocate(recon) + if (allocated(flux)) deallocate(flux) + + print *, "" + print *, "=== Component manager test completed successfully ===" + +end program test_component_manager \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_config_physics.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_config_physics.f90 new file mode 100644 index 00000000..c6fef5c0 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_config_physics.f90 @@ -0,0 +1,141 @@ +! tests/test_config_physics.f90 (修复版) +program test_config_physics + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + + implicit none + + type(cfd_config) :: config + + print *, "=== Configuration Physics Test (Simplified) ===" + print *, "" + + ! 测试1: 默认配置 + print *, "1. Testing default configuration..." + print *, "-----------------------------------" + call config_print(config) + print *, "" + + ! 测试2: 验证基础物理字段 + print *, "2. Testing basic physics fields..." + print *, "----------------------------------" + + print *, "Verifying default physics fields:" + + if (trim(config%equation_type) == "linear_advection") then + print *, " ✓ Default equation type: linear_advection" + else + print *, " ✗ Unexpected equation type: ", trim(config%equation_type) + end if + + if (trim(config%problem_type) == "linear_advection") then + print *, " ✓ Default problem type: linear_advection" + else + print *, " ✗ Unexpected problem type: ", trim(config%problem_type) + end if + + if (abs(config%domain_length - 2.0_wp) < 1e-10_wp) then + print *, " ✓ Default domain length: 2.0" + else + print *, " ✗ Unexpected domain length: ", config%domain_length + end if + + if (config%enable_physics) then + print *, " ✓ Physics enabled by default" + else + print *, " ✗ Physics not enabled by default" + end if + + print *, "" + + ! 测试3: 使用类型绑定的方法(正确的方法名) + print *, "3. Testing type-bound procedures..." + print *, "--------------------------------------" + + call config%set_physics_parameters( & + equation_type="burgers_equation", & + problem_type="sod_shock_tube", & + domain_length=3.0_wp, & + enable_physics=.false.) + + print *, "After set_physics_parameters:" + print *, " Equation type: ", trim(config%equation_type) + print *, " Problem type: ", trim(config%problem_type) + print *, " Domain length: ", config%domain_length + print *, " Physics enabled: ", config%enable_physics + + if (trim(config%equation_type) == "burgers_equation") then + print *, " ✓ Equation type modified successfully via set_physics_parameters" + end if + + if (trim(config%problem_type) == "sod_shock_tube") then + print *, " ✓ Problem type modified successfully via set_physics_parameters" + end if + + if (abs(config%domain_length - 3.0_wp) < 1e-10_wp) then + print *, " ✓ Domain length modified successfully via set_physics_parameters" + end if + + if (.not. config%enable_physics) then + print *, " ✓ Physics disabled successfully via set_physics_parameters" + end if + + print *, "" + + ! 测试4: 调用get_physics_info方法 + print *, "4. Testing get_physics_info method..." + print *, "--------------------------------------" + call config%get_physics_info() + print *, "" + + ! 测试5: 高斯脉冲配置 + print *, "5. Testing Gaussian pulse configuration..." + print *, "-----------------------------------------" + + config%ic_type = "gaussian" + config%pulse_center = 0.6_wp + config%pulse_width = 0.15_wp + + print *, "Gaussian pulse parameters:" + print *, " IC type: ", trim(config%ic_type) + print *, " Center: ", config%pulse_center + print *, " Width: ", config%pulse_width + + if (trim(config%ic_type) == "gaussian") then + print *, " ✓ Gaussian IC type set" + end if + + if (abs(config%pulse_center - 0.6_wp) < 1e-10_wp) then + print *, " ✓ Pulse center set" + end if + + if (abs(config%pulse_width - 0.15_wp) < 1e-10_wp) then + print *, " ✓ Pulse width set" + end if + + print *, "" + + ! 测试6: 重构配置 + print *, "6. Testing reconstruction configuration..." + print *, "------------------------------------------" + + call config_with_reconstruction(config, "weno", 5) + + print *, "Reconstruction configuration:" + print *, " Scheme: ", trim(config%recon_scheme) + print *, " Order: ", config%spatial_order + + if (trim(config%recon_scheme) == "weno" .and. config%spatial_order == 5) then + print *, " ✓ WENO5 configuration successful" + else + print *, " ✗ Reconstruction configuration failed" + end if + + print *, "" + + print *, "=== Configuration Physics Test Complete ===" + print *, "✓ Config module updated with physics support" + print *, "✓ Fields can be directly accessed and modified" + print *, "✓ Type-bound procedures work correctly" + +end program test_config_physics \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_domain_solution.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_domain_solution.f90 new file mode 100644 index 00000000..ff659bac --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_domain_solution.f90 @@ -0,0 +1,102 @@ +! tests/test_domain_solution.f90 +program test_domain_solution + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create, solution_reset + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + real(wp), allocatable :: initial_values(:) + integer :: i + + print *, "=== Domain and Solution Test ===" + print *, "" + + ! 测试1: 不同重构方案的ghost层计算 + print *, "1. Testing ghost layer calculation..." + print *, "--------------------------------------" + + ! ENO3 + call config_with_reconstruction(config, "eno", 3) + config%verbose = .false. + call mesh%init(ncells=10) + domain = domain_create(config, mesh) + print *, "ENO3: nghosts = ", domain%nghosts, " (expected: 3)" + + ! WENO3 + call config_with_reconstruction(config, "weno3", 3) + domain = domain_create(config, mesh) + print *, "WENO3: nghosts = ", domain%nghosts, " (expected: 2)" + + ! WENO5 + call config_with_reconstruction(config, "weno", 5) + domain = domain_create(config, mesh) + print *, "WENO5: nghosts = ", domain%nghosts, " (expected: 3)" + print *, "" + + ! 测试2: Solution数组 + print *, "2. Testing solution arrays..." + print *, "------------------------------" + + call config_with_reconstruction(config, "eno", 3) + config%verbose = .true. + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + domain = domain_create(config, mesh) + call domain%print_info() + print *, "" + + solution = solution_create(domain) + call solution%print_info() + print *, "" + + ! 测试3: 初始化和更新 + print *, "3. Testing initialization and update..." + print *, "----------------------------------------" + + allocate(initial_values(mesh%ncells)) + do i = 1, mesh%ncells + initial_values(i) = sin(2.0_wp * 3.14159265358979_wp * mesh%xcc(i) / mesh%L) + end do + + call solution%initialize(initial_values) + print *, "After initialization:" + print *, " u range: ", minval(solution%u), " to ", maxval(solution%u) + print *, " un range: ", minval(solution%un), " to ", maxval(solution%un) + + ! 修改当前解,测试更新 + solution%u = solution%u * 2.0_wp + call solution%update_old_field() + print *, "After update: max|u - un| = ", maxval(abs(solution%u - solution%un)) + print *, "" + + ! 测试4: 重置 + print *, "4. Testing reset..." + print *, "-------------------" + + call solution_reset(solution) + print *, "After reset:" + print *, " u max: ", maxval(abs(solution%u)) + print *, " un max: ", maxval(abs(solution%un)) + print *, " flux max: ", maxval(abs(solution%flux)) + print *, "" + + deallocate(initial_values) + + print *, "=== Test Summary ===" + print *, "✓ Ghost layer calculation works" + print *, "✓ Domain creation works" + print *, "✓ Solution arrays work" + print *, "✓ Initialization works" + print *, "✓ Field update works" + print *, "✓ Reset works" + print *, "" + print *, "Ready for next step: Implementing Physics modules" + +end program test_domain_solution \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_factory_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_factory_simple.f90 new file mode 100644 index 00000000..db65da7c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_factory_simple.f90 @@ -0,0 +1,58 @@ +! tests/test_factory_simple.f90 (修复版) +program test_factory_simple + use base_modules, only: wp ! ← 添加这行 + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Basic systems + print *, "1. Testing basic systems..." + print *, "-----------------------------" + call config_print(config) + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Creating reconstructors + print *, "2. Testing reconstructors..." + print *, "------------------------------" + + ! 创建并测试ENO重构器 + print *, "Creating ENO reconstructor..." + eno = eno_reconstructor() ! 使用构造函数 + call eno%info() ! 必须调用info方法 + + print *, "" + print *, "Creating WENO3 reconstructor..." + weno3 = weno3_reconstructor() ! 使用构造函数 + call weno3%info() ! 必须调用info方法 + print *, "" + + ! Test 3: Creating flux calculator + print *, "3. Testing flux calculator..." + print *, "-------------------------------" + + print *, "Creating Rusanov flux calculator..." + rusanov = rusanov_flux() ! 使用构造函数 + call rusanov%info() ! 必须调用info方法 + print *, "" + + print *, "=== Factory pattern simple test completed successfully ===" + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_minimal_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_minimal_simple.f90 new file mode 100644 index 00000000..ec03ccf8 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_minimal_simple.f90 @@ -0,0 +1,87 @@ +! tests/test_minimal_simple.f90 +program test_minimal_simple + use base_modules, only: wp ! ← 添加这行 + use registry_module + use config_module + use mesh_module + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Simple Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call registry_init() + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call list_components() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + + if (has_component_simple("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component_simple("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components (simple version) + print *, "5. Testing registry functionality" + print *, "---------------------------------" + print *, "Registry initialized: ", registry_is_initialized() + print *, "Number of components: ", registry_get_size() + print *, "" + + ! Cleanup + call registry_cleanup() + + print *, "=== Simple test completed successfully ===" + +end program test_minimal_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_physics_minimal.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_physics_minimal.f90 new file mode 100644 index 00000000..cf2a28f1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_physics_minimal.f90 @@ -0,0 +1,91 @@ +! tests/test_physics_minimal.f90 +program test_physics_minimal + use precision_module, only: wp, ip + use linear_convection_equation, only: linear_convection_eq, create_linear_convection_eq + use linear_convection_problem, only: linear_convection_prob, create_linear_convection_prob + + implicit none + + type(linear_convection_eq) :: eq + type(linear_convection_prob) :: prob + real(wp) :: u, f, a + real(wp), allocatable :: x(:), u_ic(:), u_exact(:) + integer :: i, nx = 10 + + print *, "=== 最小物理模块测试 ===" + print *, "" + + ! 测试1: 方程功能 + print *, "1. 测试方程功能..." + print *, "-------------------" + + eq = create_linear_convection_eq(wave_speed=2.0_wp) + print *, "方程: ", eq%name + print *, "波速: ", eq%wave_speed + + u = 1.5_wp + f = eq%flux(u) + a = eq%speed() + + print *, "u = ", u + print *, "F(u) = ", f, " (期望: 3.0)" + print *, "波速 a = ", a, " (期望: 2.0)" + + if (abs(f - 3.0_wp) < 1e-10_wp .and. abs(a - 2.0_wp) < 1e-10_wp) then + print *, "✓ 方程功能正常" + else + print *, "✗ 方程功能异常" + end if + print *, "" + + ! 测试2: 问题功能 + print *, "2. 测试问题功能..." + print *, "-------------------" + + prob = create_linear_convection_prob(ic_type="step", domain_length=2.0_wp) + print *, "问题: ", prob%name + print *, "IC类型: ", trim(prob%ic_type) + print *, "域长度: ", prob%domain_length + + allocate(x(nx), u_ic(nx), u_exact(nx)) + do i = 1, nx + x(i) = 0.0_wp + (i-1) * 0.2_wp + end do + + ! 测试初始条件 + call prob%initial_condition(x, u_ic) + print *, "初始条件范围: ", minval(u_ic), " 到 ", maxval(u_ic) + + ! 测试精确解 + u_exact = prob%exact_solution(x, 0.0_wp) + print *, "t=0时精确解范围: ", minval(u_exact), " 到 ", maxval(u_exact) + + ! 检查阶跃函数 + if (abs(u_ic(1) - 1.0_wp) < 1e-10_wp .and. & + abs(u_ic(6) - 2.0_wp) < 1e-10_wp) then + print *, "✓ 阶跃初始条件正确" + else + print *, "✗ 阶跃初始条件错误" + end if + + ! 检查精确解与初始条件一致 + if (maxval(abs(u_ic - u_exact)) < 1e-10_wp) then + print *, "✓ t=0时精确解与初始条件一致" + else + print *, "✗ 精确解计算错误" + end if + print *, "" + + ! 测试3: 边界条件接口 + print *, "3. 测试边界条件接口..." + print *, "----------------------" + call prob%boundary_condition(u_ic, 0.0_wp) + print *, "✓ 边界条件接口正常" + print *, "" + + deallocate(x, u_ic, u_exact) + + print *, "=== 物理模块最小测试完成 ===" + print *, "下一步: 将物理模块集成到现有系统中" + +end program test_physics_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_simple_link.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_simple_link.f90 new file mode 100644 index 00000000..71cc614e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_simple_link.f90 @@ -0,0 +1,78 @@ +! tests/test_simple_link.f90 +program test_simple_link + use base_modules, only: wp ! ← 添加这行 + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call registry_init() + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call list_components() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component_simple("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component_simple("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Cleanup + call registry_cleanup() + + print *, "=== Minimal test completed successfully ===" + +end program test_simple_link \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_solver_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_solver_base.f90 new file mode 100644 index 00000000..6cfe47e4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_solver_base.f90 @@ -0,0 +1,99 @@ +! tests/test_solver_base.f90 (修复版) +program test_solver_base + ! 所有 USE 语句必须在程序开始处 + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + use mesh_module, only: mesh_type + use solver_base_module, only: solver_base, SOLVER_UNINITIALIZED, & + SOLVER_INITIALIZED, SOLVER_COMPLETED, SOLVER_ERROR + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(solver_base) :: solver + integer :: state + + print *, "=== Solver Base Test ===" + print *, "" + + ! 测试1: 创建求解器 + print *, "1. Creating solver..." + print *, "----------------------" + + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%dt = 0.01_wp + + call config_print(config) + + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + solver = solver_base(config, mesh) + call solver%print_info() + print *, "" + + ! 测试2: 初始化 + print *, "2. Initializing solver..." + print *, "-------------------------" + + call solver%initialize() + state = solver%get_state() + print *, "State after initialization: ", state + print *, "Expected: ", SOLVER_INITIALIZED + print *, "Match? ", state == SOLVER_INITIALIZED + print *, "Error message: '", trim(solver%get_error()), "'" + print *, "" + + ! 测试3: 运行求解器 + print *, "3. Running solver..." + print *, "--------------------" + + call solver%run_to_time(0.05_wp) + state = solver%get_state() + print *, "State after run: ", state + print *, "Expected: ", SOLVER_COMPLETED + print *, "Match? ", state == SOLVER_COMPLETED + print *, "Current time: ", solver%current_time + print *, "Current step: ", solver%current_step + print *, "" + + ! 测试4: 再次运行(从已完成状态) + print *, "4. Running again from completed state..." + print *, "----------------------------------------" + + ! 需要先清理才能重新运行 + call solver%cleanup() + call solver%initialize() + call solver%run_to_time(0.1_wp) + + call solver%print_info() + print *, "" + + ! 测试5: 错误处理 + print *, "5. Testing error states..." + print *, "--------------------------" + + ! 创建一个未初始化的求解器 + call solver%cleanup() + state = solver%get_state() + print *, "Uninitialized state: ", state + print *, "Expected: ", SOLVER_UNINITIALIZED + print *, "Match? ", state == SOLVER_UNINITIALIZED + + ! 尝试运行未初始化的求解器 + call solver%run_to_time(0.01_wp) + state = solver%get_state() + print *, "State after error: ", state + print *, "Expected: ", SOLVER_ERROR + print *, "Match? ", state == SOLVER_ERROR + print *, "Error message: '", trim(solver%get_error()), "'" + print *, "" + + print *, "=== Solver Base Test Complete ===" + print *, "✓ Solver base class works" + print *, "✓ State management works" + print *, "✓ Time stepping framework works" + print *, "✓ Error handling works" + +end program test_solver_base \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_solver_framework.f90 b/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_solver_framework.f90 new file mode 100644 index 00000000..6754323d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03/tests/test_solver_framework.f90 @@ -0,0 +1,91 @@ +! tests/test_solver_framework.f90 +program test_solver_framework + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use mesh_module, only: mesh_type + use solver_module, only: cfd_solver, solver_create, solver_run, solver_cleanup + use solver_module, only: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, & + SOLVER_COMPLETED, SOLVER_ERROR + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(cfd_solver) :: solver + + print *, "=== 求解器框架测试 ===" + print *, "" + + ! 测试1: 基本创建 + print *, "1. 测试求解器创建..." + print *, "----------------------" + + ! 创建配置 + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.0_real64 + config%dt = 0.01_real64 + + call config_print(config) + print *, "" + + ! 创建网格 + call mesh%init(xmin=0.0_real64, xmax=2.0_real64, ncells=20) + call mesh%print_info() + print *, "" + + ! 创建求解器 + solver = solver_create(config, mesh) + print *, "✓ 求解器创建成功" + print *, " 状态: ", solver%get_state() + print *, "" + + ! 测试2: 求解器初始化 + print *, "2. 测试求解器初始化..." + print *, "------------------------" + + call solver%initialize() + print *, "✓ 求解器初始化完成" + print *, " 状态: ", solver%get_state() + print *, " 错误信息: '", trim(solver%get_error()), "'" + print *, "" + + ! 测试3: 简单运行 + print *, "3. 测试求解器运行..." + print *, "----------------------" + + call solver_run(solver, 0.05_real64) ! 运行到0.05秒 + print *, "✓ 求解器运行完成" + print *, " 最终状态: ", solver%get_state() + print *, "" + + ! 测试4: 清理 + print *, "4. 测试求解器清理..." + print *, "----------------------" + + call solver_cleanup(solver) + print *, "✓ 求解器清理完成" + print *, " 状态: ", solver%get_state() + print *, "" + + ! 测试5: 错误处理 + print *, "5. 测试错误处理..." + print *, "-------------------" + + ! 尝试重复初始化 + call solver%initialize() + print *, " 重复初始化状态: ", solver%get_state() + print *, " 错误信息: '", trim(solver%get_error()), "'" + + call solver_cleanup(solver) + print *, "" + + print *, "=== 框架测试总结 ===" + print *, "✓ 求解器创建/初始化/运行/清理流程验证完成" + print *, "✓ 状态管理正常工作" + print *, "✓ 错误处理机制就绪" + print *, "" + print *, "下一步: 添加实际数值计算功能" + +end program test_solver_framework \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03a/CMakeLists.txt new file mode 100644 index 00000000..ef66d584 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/README.md b/example/1d-linear-convection/weno3/fortran/registry/03a/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/03a/scripts/build.bat new file mode 100644 index 00000000..6fd6dc03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/scripts/build.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Project Builder +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python build script with full Intel environment support... +echo. + +python build.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Build failed + pause + exit /b 1 +) + +echo. +echo [INFO] Build completed successfully! +echo. +echo [INFO] To run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/03a/scripts/build.py new file mode 100644 index 00000000..3bf6d537 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/scripts/build.py @@ -0,0 +1,629 @@ +#!/usr/bin/env python3 +""" +Fortran CFD Project Builder - 完整Python解决方案 +在Python内部处理Intel oneAPI环境配置 +""" + +import os +import sys +import subprocess +import shutil +import argparse +import time +import platform +import tempfile +from pathlib import Path + +class IntelEnvironment: + """Intel oneAPI环境管理器""" + + def __init__(self): + self.setvars_path = None + self.env_vars = {} + + def find_setvars(self): + """查找setvars.bat文件""" + possible_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + r"C:\Program Files (x86)\Intel\oneAPI\compiler\latest\env\vars.bat", + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\setvars.bat"), + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\compiler\latest\env\vars.bat"), + ] + + for path in possible_paths: + if os.path.exists(path): + self.setvars_path = path + return True + + return False + + def setup_environment(self): + """设置Intel环境""" + if not self.find_setvars(): + return False + + try: + # 创建临时的批处理文件来捕获环境变量 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + f.write(f'@echo off\n') + f.write(f'call "{self.setvars_path}" >nul 2>&1\n') + f.write(f'set\n') # 输出所有环境变量 + temp_bat = f.name + + # 运行批处理文件并捕获输出 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + shell=True + ) + + # 解析环境变量 + for line in result.stdout.split('\n'): + line = line.strip() + if '=' in line: + key, value = line.split('=', 1) + self.env_vars[key.strip()] = value.strip() + + # 清理临时文件 + os.unlink(temp_bat) + + # 更新当前进程的环境变量 + os.environ.update(self.env_vars) + + return True + + except Exception as e: + print(f"设置Intel环境失败: {e}") + return False + + def get_compiler_info(self): + """获取编译器信息""" + info = {} + + # 检查ifx编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + env={**os.environ, **self.env_vars} if self.env_vars else os.environ + ) + + if result.returncode == 0: + for line in result.stdout.split('\n'): + if 'Version' in line or '版本' in line: + info['ifx_version'] = line.strip() + break + except: + pass + + # 检查环境变量 + info['ifx_root'] = self.env_vars.get('IFX_ROOT', '') + info['compiler_root'] = self.env_vars.get('ONEAPI_ROOT', '') + + return info + +class BuildSystem: + """构建系统主类""" + + def __init__(self): + self.project_root = Path(__file__).parent.parent + self.build_dir = self.project_root / "build" + self.intel_env = IntelEnvironment() + + # 设置控制台编码 + if sys.platform == "win32": + try: + import ctypes + # 设置控制台输出为UTF-8 + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + except: + pass + + def print_header(self, text): + """打印标题""" + print(f"\n{'='*70}") + print(f" {text}") + print(f"{'='*70}\n") + + def print_step(self, step, total, message): + """打印步骤""" + print(f"[{step}/{total}] {message}...") + + def print_success(self, message): + """打印成功""" + print(f"\033[92m✓ {message}\033[0m") + + def print_error(self, message): + """打印错误""" + print(f"\033[91m✗ {message}\033[0m") + + def print_warning(self, message): + """打印警告""" + print(f"\033[93m! {message}\033[0m") + + def print_info(self, message): + """打印信息""" + print(f"\033[94mℹ {message}\033[0m") + + def check_prerequisites(self): + """检查前提条件""" + self.print_step(1, 6, "检查前提条件") + + # 检查Python版本 + python_version = sys.version.split()[0] + self.print_info(f"Python版本: {python_version}") + + # 检查平台 + self.print_info(f"平台: {platform.system()} {platform.release()}") + self.print_info(f"处理器核心数: {os.cpu_count()}") + + # 检查CMake + try: + result = subprocess.run( + ["cmake", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + version_line = result.stdout.split('\n')[0] + self.print_success(f"CMake: {version_line}") + else: + self.print_error("CMake未找到") + return False + except FileNotFoundError: + self.print_error("CMake未安装") + return False + + return True + + def setup_intel_environment(self, args): + """设置Intel环境""" + self.print_step(2, 6, "配置Intel oneAPI环境") + + if not self.intel_env.find_setvars(): + self.print_warning("未找到Intel oneAPI setvars.bat") + self.print_info("将尝试使用系统环境中的编译器") + + # 检查是否能直接访问编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + self.print_success("Intel编译器在系统PATH中找到") + return True + else: + self.print_warning("Intel编译器未在PATH中找到") + except: + self.print_warning("无法访问Intel编译器") + + return True # 继续,让CMake自己找编译器 + + # 设置环境 + if self.intel_env.setup_environment(): + compiler_info = self.intel_env.get_compiler_info() + + if compiler_info.get('ifx_version'): + self.print_success(f"Intel Fortran编译器: {compiler_info['ifx_version']}") + elif compiler_info.get('ifx_root'): + self.print_success(f"Intel编译器路径: {compiler_info['ifx_root']}") + else: + self.print_success("Intel oneAPI环境配置完成") + + return True + else: + self.print_warning("Intel环境配置失败,将继续使用系统环境") + return True + + def clean_build_directory(self, args): + """清理构建目录""" + if args.clean and self.build_dir.exists(): + self.print_info("清理构建目录...") + try: + shutil.rmtree(self.build_dir) + self.print_success("构建目录已清理") + except Exception as e: + self.print_error(f"清理失败: {e}") + if not args.force: + return False + return True + + def run_command(self, cmd, cwd=None, check=True, env=None): + """运行命令""" + if isinstance(cmd, list): + cmd_str = ' '.join(str(c) for c in cmd if c) + else: + cmd_str = str(cmd) + + print(f" \033[96m$\033[0m {cmd_str}") + + try: + # 合并环境变量 + exec_env = os.environ.copy() + if env: + exec_env.update(env) + if self.intel_env.env_vars: + exec_env.update(self.intel_env.env_vars) + + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=False, + env=exec_env + ) + + # 处理输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or '完成' in line or '生成' in line: + print(f" \033[92m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + if check and result.returncode != 0: + self.print_error(f"命令执行失败,退出码: {result.returncode}") + return False + + return True + + except Exception as e: + self.print_error(f"命令执行异常: {e}") + return False + + def configure_cmake(self, args): + """配置CMake""" + self.print_step(3, 6, "配置CMake项目") + + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ] + + if args.compiler == "ifx": + cmake_cmd.extend(["-T", "fortran=ifx"]) + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + success = self.run_command(cmake_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("CMake配置完成") + else: + self.print_error("CMake配置失败") + + return success + + def build_project(self, args): + """构建项目""" + self.print_step(4, 6, "构建项目") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + success = self.run_command(build_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("项目构建完成") + else: + self.print_error("构建失败") + + return success + + def run_tests_with_environment(self, test_exe): + """运行单个测试,确保有Intel环境""" + try: + # 创建临时的批处理文件来运行测试 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'"{test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'"{test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + return result + + except Exception as e: + print(f"运行测试失败: {e}") + return None + + def run_tests(self, args): + """运行测试""" + self.print_step(5, 6, "运行测试") + + # 查找测试可执行文件 + test_dir = self.build_dir / "bin" / args.build_type + if not test_dir.exists(): + test_dir = self.build_dir / "bin" + if not test_dir.exists(): + test_dir = self.build_dir + + test_files = list(test_dir.glob("test_*.exe")) + + if not test_files: + self.print_warning("未找到测试程序") + return True + + all_passed = True + + for test_exe in sorted(test_files): + test_name = test_exe.stem + self.print_info(f"运行测试: {test_name}") + print(f" {'-'*50}") + + # 运行测试 + result = self.run_tests_with_environment(str(test_exe)) + + if result is None: + self.print_error(f" {test_name} 运行失败") + all_passed = False + continue + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + print(f" {line}") + + if result.returncode == 0: + self.print_success(f" {test_name} 通过") + else: + self.print_error(f" {test_name} 失败 (退出码: {result.returncode})") + all_passed = False + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + print() # 空行 + + return all_passed + + def create_test_runner(self, args): + """创建独立的测试运行器""" + self.print_step(6, 6, "创建测试运行器") + + runner_path = self.build_dir / "run_tests.bat" + + content = f'''@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Test Runner +echo ======================================== +echo. + +REM Setup Intel oneAPI environment +set "SETVARS_PATH={self.intel_env.setvars_path or ''}" +if exist "%SETVARS_PATH%" ( + call "%SETVARS_PATH%" >nul + echo [INFO] Intel environment configured +) else ( + echo [WARNING] Intel environment not found + echo [WARNING] Tests may fail without runtime libraries +) + +echo. + +REM Run all test executables +set "TEST_COUNT=0" +set "PASS_COUNT=0" + +for %%f in ("bin\\{args.build_type}\\test_*.exe") do ( + set /a TEST_COUNT+=1 + echo [TEST %%f] + echo {'-'*50} + + %%f + if errorlevel 1 ( + echo [FAILED] %%f + ) else ( + echo [PASSED] %%f + set /a PASS_COUNT+=1 + ) + echo. +) + +echo ======================================== +echo Tests: %PASS_COUNT%/%TEST_COUNT% passed +if %PASS_COUNT% equ %TEST_COUNT% ( + echo [SUCCESS] All tests passed! +) else ( + echo [FAILURE] Some tests failed +) +echo ======================================== + +pause +''' + + with open(runner_path, 'w', encoding='utf-8') as f: + f.write(content) + + self.print_success(f"测试运行器已创建: {runner_path}") + self.print_info(f"使用方法: cd build && run_tests.bat") + + return runner_path + + def generate_report(self, args, build_time, tests_passed): + """生成构建报告""" + self.print_header("构建完成") + + print(f"项目: {self.project_root.name}") + print(f"构建类型: {args.build_type}") + print(f"编译器: {args.compiler}") + print(f"并行作业: {args.jobs}") + print(f"总耗时: {build_time:.1f}秒") + print(f"测试结果: {'全部通过' if tests_passed else '有失败'}") + + # 显示生成的可执行文件 + bin_dir = self.build_dir / "bin" / args.build_type + if bin_dir.exists(): + print(f"\n生成的可执行文件:") + for exe in sorted(bin_dir.glob("*.exe")): + size_mb = exe.stat().st_size / (1024 * 1024) + print(f" • {exe.name} ({size_mb:.2f} MB)") + + # 显示测试运行器信息 + runner_path = self.build_dir / "run_tests.bat" + if runner_path.exists(): + print(f"\n独立测试运行器:") + print(f" • {runner_path.name}") + print(f" 在Intel oneAPI环境中运行所有测试") + + def run(self): + """运行构建系统""" + parser = argparse.ArgumentParser( + description="Fortran CFD项目构建工具", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认构建 + %(prog)s --clean # 清理后构建 + %(prog)s --build-type Release # Release构建 + %(prog)s --no-tests # 只构建,不运行测试 + %(prog)s -j8 --verbose # 8线程并行构建,详细输出 + """ + ) + + parser.add_argument("--build-type", choices=["Debug", "Release"], + default="Debug", help="构建类型") + parser.add_argument("--compiler", choices=["ifx", "ifort"], + default="ifx", help="Fortran编译器") + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-tests", action="store_true", + help="跳过测试") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + + args = parser.parse_args() + + # 开始构建 + start_time = time.time() + + self.print_header("Fortran CFD 项目构建系统") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 检查前提条件 + if not self.check_prerequisites(): + if not args.force: + return 1 + + # 2. 设置Intel环境 + if not self.setup_intel_environment(args): + if not args.force: + return 1 + + # 3. 清理目录 + if not self.clean_build_directory(args): + if not args.force: + return 1 + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 4. 配置CMake + if not self.configure_cmake(args): + if not args.force: + return 1 + + # 5. 构建项目 + if not self.build_project(args): + if not args.force: + return 1 + + # 6. 运行测试和创建测试运行器 + tests_passed = True + if not args.no_tests: + tests_passed = self.run_tests(args) + + # 创建测试运行器 + self.create_test_runner(args) # 传递 args 参数 + + # 7. 生成报告 + build_time = time.time() - start_time + self.generate_report(args, build_time, tests_passed) + + return 0 if tests_passed else 1 + + except KeyboardInterrupt: + self.print_error("\n构建被用户中断") + return 1 + except Exception as e: + self.print_error(f"构建过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + builder = BuildSystem() + return builder.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/03a/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/scripts/run_all_steps.bat b/example/1d-linear-convection/weno3/fortran/registry/03a/scripts/run_all_steps.bat new file mode 100644 index 00000000..d506149b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/scripts/run_all_steps.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo CFD Project: All Steps +echo ======================================== +echo. + +echo [INFO] Starting Step 1: Physics Modules Test... +call run_step1.bat + +if errorlevel 1 ( + echo [ERROR] Step 1 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Starting Step 2: Configuration Physics Update... +call run_step2.bat + +if errorlevel 1 ( + echo [ERROR] Step 2 failed + pause + exit /b 1 +) + +echo. +echo ======================================== +echo All Steps Completed Successfully! +echo ======================================== +echo. +echo [INFO] Next: Update component manager for physics support +echo [INFO] Run: run_step3.bat (to be created) +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/scripts/run_step1.bat b/example/1d-linear-convection/weno3/fortran/registry/03a/scripts/run_step1.bat new file mode 100644 index 00000000..0b6b1f17 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/scripts/run_step1.bat @@ -0,0 +1,39 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Step 1: Physics Modules Test +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python step1 script with full Intel environment support... +echo. + +python run_step1.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Step 1 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Step 1 completed successfully! +echo. +echo [INFO] Next step: Update config to include physics settings +echo [INFO] Run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/scripts/run_step1.py b/example/1d-linear-convection/weno3/fortran/registry/03a/scripts/run_step1.py new file mode 100644 index 00000000..5e087a69 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/scripts/run_step1.py @@ -0,0 +1,319 @@ +#!/usr/bin/env python3 +""" +Step 1: Physics Modules Test +扩展build.py,专门用于测试物理模块 +""" + +import os +import sys +import subprocess +import time +from pathlib import Path + +# 添加当前目录到路径,以便导入build.py的类 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import IntelEnvironment, BuildSystem +except ImportError: + print("Error: Cannot import build.py. Make sure build.py is in the same directory.") + sys.exit(1) + +class Step1System(BuildSystem): + """Step 1 测试系统,继承自BuildSystem""" + + def __init__(self): + super().__init__() + self.test_name = "test_physics_minimal" + self.test_exe = None + + def find_test_executable(self): + """查找测试可执行文件""" + possible_paths = [ + self.build_dir / "bin" / "Debug" / f"{self.test_name}.exe", + self.build_dir / "Debug" / f"{self.test_name}.exe", + self.build_dir / f"{self.test_name}.exe", + self.build_dir / "bin" / f"{self.test_name}.exe", + ] + + for path in possible_paths: + if path.exists(): + self.test_exe = path + self.print_success(f"Found test executable: {path}") + return True + + # 如果没有找到,尝试搜索 + self.print_warning(f"Could not find {self.test_name}.exe") + self.print_info("Searching for test executables...") + + try: + result = subprocess.run( + ["dir", str(self.build_dir), "/s", "/b", "*.exe"], + capture_output=True, + text=True, + encoding='utf-8', + shell=True + ) + + if result.returncode == 0: + test_files = [line.strip() for line in result.stdout.split('\n') + if line and 'test_' in line.lower()] + + if test_files: + self.print_info("Found test files:") + for test_file in test_files: + self.print_info(f" {test_file}") + return False + except: + pass + + return False + + def run_test_with_intel_env(self): + """在Intel环境下运行测试""" + if not self.test_exe: + self.print_error("No test executable found") + return False + + self.print_step(1, 2, f"Running test: {self.test_exe.name}") + + try: + # 创建临时的批处理文件来运行测试(包含Intel环境) + import tempfile + + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + # 设置Intel环境 + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'echo [INFO] Intel environment configured\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'echo [WARNING] Intel environment not found\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + self.print_info(f"Command: {temp_bat}") + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower() or 'fail' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or 'pass' in line.lower() or '✓' in line: + print(f" \033[92m{line}\033[0m") + elif '=' in line or '---' in line: + print(f" \033[96m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + self.print_warning("Test stderr output:") + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + self.print_step(2, 2, "Test execution completed") + + if result.returncode == 0: + self.print_success("Test passed") + return True + else: + self.print_error(f"Test failed (exit code: {result.returncode})") + return False + + except Exception as e: + self.print_error(f"Failed to run test: {e}") + return False + + def build_project_if_needed(self, args): + """如果需要,构建项目""" + if args.no_build: + self.print_info("Skipping build (--no-build flag)") + return True + + self.print_step(1, 3, "Building project") + + # 调用父类的构建方法 + build_args = argparse.Namespace() + build_args.clean = args.clean + build_args.build_type = "Debug" + build_args.compiler = "ifx" + build_args.no_tests = True # 不运行所有测试 + build_args.jobs = os.cpu_count() + build_args.verbose = args.verbose + build_args.force = args.force + + # 清理构建目录 + if args.clean and self.build_dir.exists(): + self.print_info("Cleaning build directory...") + import shutil + try: + shutil.rmtree(self.build_dir) + self.print_success("Build directory cleaned") + except Exception as e: + self.print_error(f"Clean failed: {e}") + if not args.force: + return False + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 配置CMake + self.print_step(2, 3, "Configuring CMake") + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-DCMAKE_BUILD_TYPE=Debug", + "-T", "fortran=ifx", + ] + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + if not self.run_command(cmake_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + # 构建项目 + self.print_step(3, 3, "Building project") + build_cmd = [ + "cmake", + "--build", ".", + "--config", "Debug", + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + if not self.run_command(build_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + self.print_success("Build completed") + return True + + def run(self): + """运行Step 1测试""" + parser = argparse.ArgumentParser( + description="Step 1: Physics Modules Test", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认运行 + %(prog)s --clean # 清理后构建并测试 + %(prog)s --no-build # 只运行测试,不重新构建 + %(prog)s --verbose # 详细输出 + %(prog)s -j4 # 使用4个并行作业构建 + """ + ) + + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-build", action="store_true", + help="不重新构建,直接运行测试") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + + args = parser.parse_args() + + # 开始测试 + start_time = time.time() + + self.print_header("Step 1: Physics Modules Implementation Test") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 设置Intel环境 + self.print_step(1, 4, "Setting up Intel oneAPI environment") + if not self.setup_intel_environment(args): + if not args.force: + self.print_error("Intel environment setup failed") + return 1 + self.print_warning("Intel environment setup failed, continuing...") + + # 2. 构建项目(如果需要) + self.print_step(2, 4, "Building project if needed") + if not self.build_project_if_needed(args): + if not args.force: + return 1 + + # 3. 查找测试可执行文件 + self.print_step(3, 4, "Finding test executable") + if not self.find_test_executable(): + if not args.force: + return 1 + self.print_warning("Test executable not found, but continuing due to --force") + return 0 + + # 4. 运行测试 + self.print_step(4, 4, "Running physics module test") + test_passed = self.run_test_with_intel_env() + + # 生成报告 + test_time = time.time() - start_time + self.print_header("Step 1 Complete") + + print(f"测试: {'通过 ✓' if test_passed else '失败 ✗'}") + print(f"测试程序: {self.test_exe.name if self.test_exe else '未找到'}") + print(f"总耗时: {test_time:.1f}秒") + + if test_passed: + print(f"\n下一步: 更新配置以包含物理设置") + print(f"建议: 修改config.f90,添加physics相关字段") + return 0 + else: + if args.force: + self.print_warning("测试失败,但由于--force标志继续执行") + return 0 + return 1 + + except KeyboardInterrupt: + self.print_error("\n测试被用户中断") + return 1 + except Exception as e: + self.print_error(f"测试过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + system = Step1System() + return system.run() + +if __name__ == "__main__": + # 需要导入argparse + import argparse + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/scripts/run_step2.bat b/example/1d-linear-convection/weno3/fortran/registry/03a/scripts/run_step2.bat new file mode 100644 index 00000000..9c1f62de --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/scripts/run_step2.bat @@ -0,0 +1,39 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Step 2: Configuration Physics Update +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python step2 script with full Intel environment support... +echo. + +python run_step2.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Step 2 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Step 2 completed successfully! +echo. +echo [INFO] Next step: Update component manager to support physics +echo [INFO] Run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/scripts/run_step2.py b/example/1d-linear-convection/weno3/fortran/registry/03a/scripts/run_step2.py new file mode 100644 index 00000000..c16b7608 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/scripts/run_step2.py @@ -0,0 +1,284 @@ +#!/usr/bin/env python3 +""" +Step 2: Configuration Physics Update +测试配置模块的物理功能更新 +""" + +import os +import sys +import subprocess +import time +from pathlib import Path + +# 添加当前目录到路径,以便导入build.py的类 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import IntelEnvironment, BuildSystem +except ImportError: + print("Error: Cannot import build.py. Make sure build.py is in the same directory.") + sys.exit(1) + +class Step2System(BuildSystem): + """Step 2 测试系统,继承自BuildSystem""" + + def __init__(self): + super().__init__() + self.test_name = "test_config_physics" + self.test_exe = None + + def find_test_executable(self): + """查找测试可执行文件""" + possible_paths = [ + self.build_dir / "bin" / "Debug" / f"{self.test_name}.exe", + self.build_dir / "Debug" / f"{self.test_name}.exe", + self.build_dir / f"{self.test_name}.exe", + self.build_dir / "bin" / f"{self.test_name}.exe", + ] + + for path in possible_paths: + if path.exists(): + self.test_exe = path + self.print_success(f"Found test executable: {path}") + return True + + return False + + def run_test_with_intel_env(self): + """在Intel环境下运行测试""" + if not self.test_exe: + self.print_error("No test executable found") + return False + + self.print_step(1, 2, f"Running test: {self.test_exe.name}") + + try: + # 创建临时的批处理文件来运行测试(包含Intel环境) + import tempfile + + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + # 设置Intel环境 + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'echo [INFO] Intel environment configured\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'echo [WARNING] Intel environment not found\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + self.print_info(f"Command: {temp_bat}") + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower() or 'fail' in line.lower() or '✗' in line: + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or 'pass' in line.lower() or '✓' in line: + print(f" \033[92m{line}\033[0m") + elif '=' in line or '---' in line or '===' in line: + print(f" \033[96m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + self.print_warning("Test stderr output:") + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + self.print_step(2, 2, "Test execution completed") + + if result.returncode == 0: + self.print_success("Test passed") + return True + else: + self.print_error(f"Test failed (exit code: {result.returncode})") + return False + + except Exception as e: + self.print_error(f"Failed to run test: {e}") + return False + + def build_project(self, args): + """构建项目""" + if args.no_build: + self.print_info("Skipping build (--no-build flag)") + return True + + self.print_step(1, 3, "Building project") + + # 清理构建目录 + if args.clean and self.build_dir.exists(): + self.print_info("Cleaning build directory...") + import shutil + try: + shutil.rmtree(self.build_dir) + self.print_success("Build directory cleaned") + except Exception as e: + self.print_error(f"Clean failed: {e}") + if not args.force: + return False + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 配置CMake + self.print_step(2, 3, "Configuring CMake") + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-DCMAKE_BUILD_TYPE=Debug", + "-T", "fortran=ifx", + ] + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + if not self.run_command(cmake_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + # 构建项目 + self.print_step(3, 3, "Building project") + build_cmd = [ + "cmake", + "--build", ".", + "--config", "Debug", + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + if not self.run_command(build_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + self.print_success("Build completed") + return True + + def run(self): + """运行Step 2测试""" + import argparse + + parser = argparse.ArgumentParser( + description="Step 2: Configuration Physics Update", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认运行 + %(prog)s --clean # 清理后构建并测试 + %(prog)s --no-build # 只运行测试,不重新构建 + %(prog)s --verbose # 详细输出 + """ + ) + + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-build", action="store_true", + help="不重新构建,直接运行测试") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + + args = parser.parse_args() + + # 开始测试 + start_time = time.time() + + self.print_header("Step 2: Configuration Physics Update") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 设置Intel环境 + self.print_step(1, 4, "Setting up Intel oneAPI environment") + if not self.setup_intel_environment(args): + if not args.force: + self.print_error("Intel environment setup failed") + return 1 + self.print_warning("Intel environment setup failed, continuing...") + + # 2. 构建项目(如果需要) + self.print_step(2, 4, "Building project if needed") + if not self.build_project(args): + if not args.force: + return 1 + + # 3. 查找测试可执行文件 + self.print_step(3, 4, "Finding test executable") + if not self.find_test_executable(): + self.print_error(f"Test executable {self.test_name}.exe not found") + if not args.force: + return 1 + self.print_warning("Test executable not found, but continuing due to --force") + return 0 + + # 4. 运行测试 + self.print_step(4, 4, "Running configuration physics test") + test_passed = self.run_test_with_intel_env() + + # 生成报告 + test_time = time.time() - start_time + self.print_header("Step 2 Complete") + + print(f"测试: {'通过 ✓' if test_passed else '失败 ✗'}") + print(f"测试程序: {self.test_exe.name if self.test_exe else '未找到'}") + print(f"总耗时: {test_time:.1f}秒") + + if test_passed: + print(f"\n下一步: 更新组件管理器以支持物理模块") + print(f"建议: 修改component_manager.f90,添加physics组件创建") + return 0 + else: + if args.force: + self.print_warning("测试失败,但由于--flag继续执行") + return 0 + return 1 + + except KeyboardInterrupt: + self.print_error("\n测试被用户中断") + return 1 + except Exception as e: + self.print_error(f"测试过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + system = Step2System() + return system.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03a/src/CMakeLists.txt new file mode 100644 index 00000000..2aaee245 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/CMakeLists.txt @@ -0,0 +1,18 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(base) +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) +add_subdirectory(physics) # ← 新增物理模块目录 +add_subdirectory(manager) +add_subdirectory(solver) + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/base/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03a/src/base/CMakeLists.txt new file mode 100644 index 00000000..74f4aa65 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/base/CMakeLists.txt @@ -0,0 +1,16 @@ +# src/base/CMakeLists.txt +message(STATUS "Configuring base module...") + +add_library(base STATIC + modules.f90 + precision.f90 # 新增 +) + +set_target_properties(base PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Base module configured") + +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/base/modules.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/src/base/modules.f90 new file mode 100644 index 00000000..43aaee24 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/base/modules.f90 @@ -0,0 +1,36 @@ +! src/base/modules.f90 +module base_modules + use, intrinsic :: iso_fortran_env, only: real64, int32 + implicit none + + public :: wp, ip, max_name_len, string_len, cfd_config_base, component_info + + integer, parameter :: wp = real64 + integer, parameter :: ip = int32 + integer, parameter :: string_len = 100 + integer, parameter :: max_name_len = 32 + + ! 基础配置类型 + type :: cfd_config_base + character(len=max_name_len) :: ic_type = "step" + character(len=max_name_len) :: recon_scheme = "eno" + character(len=max_name_len) :: flux_type = "rusanov" + integer(ip) :: rk_order = 1 + real(wp) :: wave_speed = 1.0_wp + real(wp) :: final_time = 0.625_wp + real(wp) :: dt = 0.025_wp + character(len=max_name_len) :: boundary_type = "periodic" + integer(ip) :: spatial_order = 2 + character(len=max_name_len) :: equation_type = "linear_advection" + character(len=max_name_len) :: problem_type = "linear_advection" + logical :: verbose = .true. + end type cfd_config_base + + ! 组件信息类型 + type :: component_info + character(len=max_name_len) :: category = "" + character(len=max_name_len) :: name = "" + integer(ip) :: order = 0 + end type component_info + +end module base_modules \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/base/precision.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/src/base/precision.f90 new file mode 100644 index 00000000..4ac5fd7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/base/precision.f90 @@ -0,0 +1,9 @@ +! src/base/precision.f90(简单版本) +module precision_module + use base_modules, only: wp, ip + implicit none + + ! 重新导出,确保兼容 + public :: wp, ip + +end module precision_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03a/src/core/CMakeLists.txt new file mode 100644 index 00000000..d8b8df06 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/core/CMakeLists.txt @@ -0,0 +1,14 @@ +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") + +add_library(core STATIC + registry.f90 +) + +target_link_libraries(core PRIVATE base) + +set_target_properties(core PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Core module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/core/factory_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/src/core/factory_base.f90 new file mode 100644 index 00000000..302418a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/core/factory_base.f90 @@ -0,0 +1,57 @@ +! src/core/factory_base.f90 +module factory_base_module + use base_modules, only: wp, ip + use registry_module, only: create_component, has_component + + implicit none + private + public :: wp, ip, factory_base, factory_create + + ! 工厂基类 + type :: factory_base + character(len=max_name_length) :: category = "" + contains + procedure :: create => factory_base_create + procedure :: get_available => factory_base_get_available + end type factory_base + + ! 便捷函数类型 + abstract interface + function factory_function_interface(category, name) result(instance) + import :: wp + character(len=*), intent(in) :: category, name + class(*), allocatable :: instance + end function factory_function_interface + end interface + +contains + + ! 创建工厂实例 + function factory_create(category) result(factory) + character(len=*), intent(in) :: category + type(factory_base) :: factory + factory%category = trim(category) + end function factory_create + + ! 工厂创建方法 + function factory_base_create(this, name) result(instance) + class(factory_base), intent(in) :: this + character(len=*), intent(in) :: name + class(*), allocatable :: instance + + instance = create_component(this%category, name) + end function factory_base_create + + ! 获取可用组件列表(简化版) + subroutine factory_base_get_available(this, names, count) + class(factory_base), intent(in) :: this + character(len=*), allocatable, intent(out) :: names(:) + integer(ip), intent(out) :: count + + ! 这里需要实现从注册表获取列表的逻辑 + ! 暂时返回空列表 + count = 0 + allocate(character(len=max_name_length) :: names(0)) + end subroutine factory_base_get_available + +end module factory_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..a960167c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/src/core/registry.f90 new file mode 100644 index 00000000..acc63edb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/core/registry.f90 @@ -0,0 +1,203 @@ +! src/core/registry.f90 +module registry_module + use base_modules, only: wp, ip, max_name_len, component_info + + implicit none + private + + ! 明确公开所有需要的接口 + public :: wp, ip ! 类型参数 + public :: component_info ! 类型 + public :: registry_init, registry_cleanup ! 初始化/清理 + public :: register_component_simple ! 注册组件 + public :: has_component_simple ! 检查组件 + public :: list_components ! 列出组件 + public :: registry_is_initialized ! 检查初始化状态 ← 新增 + public :: registry_get_size ! 获取大小 ← 新增 + + ! 全局注册表 + type :: component_registry + type(component_info), allocatable :: components(:) + integer(ip) :: count = 0 + integer(ip) :: capacity = 100 + logical :: initialized = .false. + logical :: verbose = .true. + end type component_registry + + type(component_registry) :: registry + +contains + + ! ==================== 公共API ==================== + + subroutine registry_init(verbose) + logical, optional, intent(in) :: verbose + + if (registry%initialized) then + if (registry%verbose) then + print *, "[REGISTRY] Already initialized" + end if + return + end if + + if (present(verbose)) then + registry%verbose = verbose + end if + + allocate(registry%components(registry%capacity)) + registry%initialized = .true. + + if (registry%verbose) then + print *, "[REGISTRY] Initialized with capacity:", registry%capacity + end if + end subroutine registry_init + + subroutine registry_cleanup() + if (allocated(registry%components)) then + deallocate(registry%components) + end if + registry%initialized = .false. + registry%count = 0 + + if (registry%verbose) then + print *, "[REGISTRY] Cleaned up" + end if + end subroutine registry_cleanup + + subroutine register_component_simple(category, name, order) + character(len=*), intent(in) :: category, name + integer(ip), optional, intent(in) :: order + + integer(ip) :: i + type(component_info) :: info + + if (.not. registry%initialized) then + call registry_init() + end if + + ! 检查是否已存在 + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + if (registry%verbose) then + print *, "[WARN] Overwriting component: ", trim(category), ".", trim(name) + end if + + ! 更新 + if (present(order)) then + registry%components(i)%order = order + else + registry%components(i)%order = 0 + end if + return + end if + end do + + ! 扩展数组 + if (registry%count >= registry%capacity) then + call expand_registry() + end if + + ! 添加新组件 + registry%count = registry%count + 1 + + info%category = trim(category) + info%name = trim(name) + info%order = 0 + if (present(order)) then + info%order = order + end if + + registry%components(registry%count) = info + + if (registry%verbose) then + print *, "[OK] Registered simple: ", trim(category), ".", trim(name) + end if + end subroutine register_component_simple + + logical function has_component_simple(category, name) + character(len=*), intent(in) :: category, name + + integer(ip) :: i + + has_component_simple = .false. + + if (.not. registry%initialized) return + + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + has_component_simple = .true. + return + end if + end do + end function has_component_simple + + subroutine list_components(category) + character(len=*), optional, intent(in) :: category + + integer(ip) :: i, count + + if (.not. registry%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + if (registry%count == 0) then + print *, "[INFO] No components registered" + return + end if + + count = 0 + print *, "=== Registry Contents ===" + do i = 1, registry%count + if (.not. present(category) .or. & + trim(registry%components(i)%category) == trim(category)) then + call print_component_info(registry%components(i)) + count = count + 1 + end if + end do + + print *, "Total:", count, "components" + print *, "==========================" + end subroutine list_components + + ! ==================== 新增函数 ==================== + + logical function registry_is_initialized() + ! 检查注册表是否已初始化 + registry_is_initialized = registry%initialized + end function registry_is_initialized + + integer(ip) function registry_get_size() + ! 获取注册表中的组件数量 + registry_get_size = registry%count + end function registry_get_size + + ! ==================== 内部辅助函数 ==================== + + subroutine expand_registry() + type(component_info), allocatable :: temp(:) + + registry%capacity = registry%capacity * 2 + allocate(temp(registry%capacity)) + temp(1:registry%count) = registry%components(1:registry%count) + call move_alloc(temp, registry%components) + + if (registry%verbose) then + print *, "[INFO] Registry expanded to capacity:", registry%capacity + end if + end subroutine expand_registry + + subroutine print_component_info(info) + type(component_info), intent(in) :: info + + if (info%order > 0) then + print *, " [", trim(info%category), ".", trim(info%name), & + " (order:", info%order, ")]" + else + print *, " [", trim(info%category), ".", trim(info%name), "]" + end if + end subroutine print_component_info + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03a/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..70cbbd2f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/infrastructure/CMakeLists.txt @@ -0,0 +1,17 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "Configuring infrastructure module...") + +add_library(infrastructure STATIC + config.f90 + mesh.f90 + domain.f90 # 新增 + solution.f90 # 新增 +) + +target_link_libraries(infrastructure PRIVATE base) + +set_target_properties(infrastructure PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Infrastructure module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/src/infrastructure/config.f90 new file mode 100644 index 00000000..7586a1a5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/infrastructure/config.f90 @@ -0,0 +1,144 @@ +! src/infrastructure/config.f90 (修复版) +module config_module + use base_modules, only: wp, ip, max_name_len, cfd_config_base + + implicit none + public :: wp, ip, cfd_config, config_print, config_with_reconstruction + + ! 扩展配置类型 - 添加物理相关字段 + type, extends(cfd_config_base) :: cfd_config + ! 物理参数 + real(wp) :: left_boundary_value = 1.0_wp + real(wp) :: right_boundary_value = 2.0_wp + real(wp) :: domain_length = 2.0_wp + + ! 新增:物理模块相关配置 + real(wp) :: pulse_center = 0.5_wp ! 高斯脉冲中心 + real(wp) :: pulse_width = 0.1_wp ! 高斯脉冲宽度 + logical :: enable_physics = .true. ! 是否启用物理模块 + contains + ! 新增:物理相关配置方法 + procedure :: set_physics_parameters + procedure :: get_physics_info + end type cfd_config + +contains + + subroutine config_print(cfg) + type(cfd_config), intent(in) :: cfg + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(cfg%ic_type) + print *, "Reconstruction: ", trim(cfg%recon_scheme), " (order:", cfg%spatial_order, ")" + print *, "Flux type: ", trim(cfg%flux_type) + print *, "Time integration: RK", cfg%rk_order + print *, "Wave speed: ", cfg%wave_speed + print *, "Final time: ", cfg%final_time + print *, "Time step: ", cfg%dt + print *, "Boundary: ", trim(cfg%boundary_type) + + ! 新增:物理配置信息 + print *, "--- Physics Configuration ---" + print *, "Equation type: ", trim(cfg%equation_type) + print *, "Problem type: ", trim(cfg%problem_type) + print *, "Domain length: ", cfg%domain_length + print *, "Physics enabled: ", cfg%enable_physics + + if (cfg%ic_type == "gaussian") then + print *, "Pulse center: ", cfg%pulse_center + print *, "Pulse width: ", cfg%pulse_width + end if + + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(cfg, scheme, order) + type(cfd_config), intent(inout) :: cfg + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + integer :: i + + ! 转换为小写 + cfg%recon_scheme = scheme + do i = 1, len_trim(cfg%recon_scheme) + if (cfg%recon_scheme(i:i) >= 'A' .and. cfg%recon_scheme(i:i) <= 'Z') then + cfg%recon_scheme(i:i) = char(ichar(cfg%recon_scheme(i:i)) + 32) + end if + end do + + ! 设置阶数 + if (present(order)) then + cfg%spatial_order = order + else + if (index(cfg%recon_scheme, 'weno') > 0) then + cfg%spatial_order = 5 + else if (trim(cfg%recon_scheme) == 'eno') then + cfg%spatial_order = 3 + end if + end if + + if (cfg%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(cfg%recon_scheme), & + " Order: ", cfg%spatial_order + end if + end subroutine config_with_reconstruction + + ! ========== 新增:物理参数设置方法 ========== + + subroutine set_physics_parameters(this, equation_type, problem_type, & + domain_length, enable_physics) + class(cfd_config), intent(inout) :: this + character(len=*), intent(in), optional :: equation_type, problem_type + real(wp), intent(in), optional :: domain_length + logical, intent(in), optional :: enable_physics + + if (present(equation_type)) then + this%equation_type = trim(equation_type) + if (this%verbose) then + print *, "[CONFIG] Set equation type: ", trim(this%equation_type) + end if + end if + + if (present(problem_type)) then + this%problem_type = trim(problem_type) + if (this%verbose) then + print *, "[CONFIG] Set problem type: ", trim(this%problem_type) + end if + end if + + if (present(domain_length)) then + this%domain_length = domain_length + if (this%verbose) then + print *, "[CONFIG] Set domain length: ", this%domain_length + end if + end if + + if (present(enable_physics)) then + this%enable_physics = enable_physics + if (this%verbose) then + print *, "[CONFIG] Physics module enabled: ", this%enable_physics + end if + end if + end subroutine set_physics_parameters + + subroutine get_physics_info(this) + class(cfd_config), intent(in) :: this + + print *, "=== Physics Configuration Info ===" + print *, "Equation type: ", trim(this%equation_type) + print *, "Problem type: ", trim(this%problem_type) + print *, "Domain length: ", this%domain_length + print *, "Wave speed: ", this%wave_speed + print *, "Physics enabled: ", this%enable_physics + + if (this%ic_type == "gaussian") then + print *, "Pulse parameters:" + print *, " Center: ", this%pulse_center + print *, " Width: ", this%pulse_width + end if + + print *, "==================================" + end subroutine get_physics_info + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/infrastructure/domain.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/src/infrastructure/domain.f90 new file mode 100644 index 00000000..c3662f03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/infrastructure/domain.f90 @@ -0,0 +1,102 @@ +! src/infrastructure/domain.f90 +module domain_module + use base_modules, only: wp, ip, max_name_len + use config_module, only: cfd_config + use mesh_module, only: mesh_type + + implicit none + private + public :: wp, ip, domain_type, domain_create, is_physical_cell + + type :: domain_type + type(cfd_config), pointer :: config => null() + type(mesh_type), pointer :: mesh => null() + integer(ip) :: nghosts = 0 + integer(ip) :: ist = 1 ! 物理区域起始索引(1-based) + integer(ip) :: ied = 1 ! 物理区域结束索引(exclusive) + integer(ip) :: ntcells = 0 ! 总单元数(含ghost) + contains + procedure :: print_info => domain_print_info + procedure :: get_physical_indices => domain_get_physical_indices + end type domain_type + +contains + + function domain_create(config, mesh) result(domain) + type(cfd_config), target, intent(in) :: config + type(mesh_type), target, intent(in) :: mesh + type(domain_type) :: domain + + domain%config => config + domain%mesh => mesh + + ! 计算ghost层数(参考Julia的_calc_nghosts) + domain%nghosts = calc_nghosts(config) + domain%ist = domain%nghosts + 1 + domain%ied = domain%ist + mesh%ncells + domain%ntcells = mesh%ncells + 2 * domain%nghosts + + if (config%verbose) then + print *, "[DOMAIN] Created:" + print *, " Ghost layers: ", domain%nghosts + print *, " Physical cells: ", domain%ist, " to ", domain%ied - 1 + print *, " Total cells: ", domain%ntcells + end if + end function domain_create + + function calc_nghosts(config) result(nghosts) + type(cfd_config), intent(in) :: config + integer(ip) :: nghosts + + character(len=max_name_len) :: scheme + + scheme = config%recon_scheme + + if (scheme == "eno") then + nghosts = config%spatial_order + else if (index(scheme, "weno") > 0) then + nghosts = config%spatial_order / 2 + 1 + else + print *, "[WARNING] Unknown scheme, using default nghosts=2" + nghosts = 2 + end if + + if (nghosts <= 0) then + print *, "[ERROR] Invalid nghosts: ", nghosts + nghosts = 2 + end if + end function calc_nghosts + + logical function is_physical_cell(this, idx) + class(domain_type), intent(in) :: this + integer(ip), intent(in) :: idx + is_physical_cell = (idx >= this%ist .and. idx < this%ied) + end function is_physical_cell + + function domain_get_physical_indices(this) result(indices) + class(domain_type), intent(in) :: this + integer(ip), allocatable :: indices(:) + integer(ip) :: i, count + + count = this%ied - this%ist + allocate(indices(count)) + + do i = 1, count + indices(i) = this%ist + i - 1 + end do + end function domain_get_physical_indices + + subroutine domain_print_info(this) + class(domain_type), intent(in) :: this + + print *, "=== Domain Information ===" + print *, "Configuration: ", trim(this%config%recon_scheme), & + " order ", this%config%spatial_order + print *, "Ghost layers: ", this%nghosts + print *, "Physical cells: ", this%ist, " to ", this%ied - 1 + print *, "Total cells: ", this%ntcells + print *, "Mesh cells: ", this%mesh%ncells + print *, "==========================" + end subroutine domain_print_info + +end module domain_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..f810f3a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/infrastructure/mesh.f90 @@ -0,0 +1,73 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use base_modules, only: wp, ip + + implicit none + public :: wp, ip, mesh_type, mesh_init, mesh_print_info + + ! 网格类型 + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer(ip) :: ncells = 40 + integer(ip) :: nnodes + integer(ip) :: nx + real(wp), allocatable :: x(:) ! 节点坐标 + real(wp), allocatable :: xcc(:) ! 单元中心坐标 + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer(ip), optional, intent(in) :: ncells + + integer(ip) :: i + + ! 设置参数 + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! 计算 + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! 分配内存 + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! 生成节点坐标 + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! 生成单元中心坐标 + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== Mesh Information ===" + print *, "Domain: [", this%xmin, ", ", this%xmax, "]" + print *, "Cells: ", this%ncells + print *, "Nodes: ", this%nnodes + print *, "dx: ", this%dx + print *, "L: ", this%L + print *, "========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/infrastructure/solution.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/src/infrastructure/solution.f90 new file mode 100644 index 00000000..ce88fd8a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/infrastructure/solution.f90 @@ -0,0 +1,131 @@ +! src/infrastructure/solution.f90 +module solution_module + use base_modules, only: wp, ip + use domain_module, only: domain_type + + implicit none + private + public :: wp, ip, solution_type, solution_create, solution_reset + + type :: solution_type + type(domain_type), pointer :: domain => null() + real(wp), allocatable :: u(:) ! 当前解(含ghost) + real(wp), allocatable :: un(:) ! 旧解 + real(wp), allocatable :: q_face_left(:) ! 左界面值 + real(wp), allocatable :: q_face_right(:)! 右界面值 + real(wp), allocatable :: flux(:) ! 通量 + real(wp), allocatable :: res(:) ! 残差 + contains + procedure :: initialize => solution_initialize + procedure :: update_old_field => solution_update_old_field + procedure :: print_info => solution_print_info + procedure :: reset => solution_reset_instance + end type solution_type + +contains + + function solution_create(domain) result(solution) + type(domain_type), target, intent(in) :: domain + type(solution_type) :: solution + + integer(ip) :: ncells, nnodes, ntcells + + solution%domain => domain + + ncells = domain%mesh%ncells + nnodes = domain%mesh%nnodes + ntcells = domain%ntcells + + ! 分配数组(与Julia solution.jl一致) + allocate(solution%u(ntcells), source=0.0_wp) + allocate(solution%un(ntcells), source=0.0_wp) + allocate(solution%q_face_left(nnodes), source=0.0_wp) + allocate(solution%q_face_right(nnodes), source=0.0_wp) + allocate(solution%flux(nnodes), source=0.0_wp) + allocate(solution%res(ncells), source=0.0_wp) + + if (domain%config%verbose) then + print *, "[SOLUTION] Created:" + print *, " u size: ", size(solution%u), " (with ghosts)" + print *, " flux size: ", size(solution%flux) + print *, " res size: ", size(solution%res) + end if + end function solution_create + + subroutine solution_initialize(this, initial_values) + class(solution_type), intent(inout) :: this + real(wp), intent(in), optional :: initial_values(:) + + integer(ip) :: i, idx + type(domain_type), pointer :: domain + + domain => this%domain + + if (present(initial_values)) then + ! 应用初始值到物理区域 + do i = domain%ist, domain%ied - 1 + idx = i - domain%ist + 1 + if (idx <= size(initial_values)) then + this%u(i) = initial_values(idx) + end if + end do + else + ! 默认为0 + this%u = 0.0_wp + end if + + ! 同步旧场(与Julia的update_old_field一致) + call this%update_old_field() + + if (domain%config%verbose) then + print *, "[SOLUTION] Initialized" + print *, " u range: ", minval(this%u), " to ", maxval(this%u) + end if + end subroutine solution_initialize + + subroutine solution_update_old_field(this) + class(solution_type), intent(inout) :: this + this%un = this%u ! 与Julia的 un .= u 一致 + end subroutine solution_update_old_field + + subroutine solution_reset_instance(this) + class(solution_type), intent(inout) :: this + call solution_reset(this) + end subroutine solution_reset_instance + + subroutine solution_reset(solution) + type(solution_type), intent(inout) :: solution + + if (allocated(solution%u)) solution%u = 0.0_wp + if (allocated(solution%un)) solution%un = 0.0_wp + if (allocated(solution%q_face_left)) solution%q_face_left = 0.0_wp + if (allocated(solution%q_face_right)) solution%q_face_right = 0.0_wp + if (allocated(solution%flux)) solution%flux = 0.0_wp + if (allocated(solution%res)) solution%res = 0.0_wp + + if (associated(solution%domain) .and. solution%domain%config%verbose) then + print *, "[SOLUTION] Reset" + end if + end subroutine solution_reset + + subroutine solution_print_info(this) + class(solution_type), intent(in) :: this + + print *, "=== Solution Information ===" + print *, "Arrays:" + print *, " u: ", size(this%u), " elements" + print *, " un: ", size(this%un), " elements" + print *, " q_face_left: ", size(this%q_face_left), " elements" + print *, " q_face_right: ", size(this%q_face_right), " elements" + print *, " flux: ", size(this%flux), " elements" + print *, " res: ", size(this%res), " elements" + + if (allocated(this%u)) then + print *, "Values:" + print *, " u min/max: ", minval(this%u), maxval(this%u) + print *, " un min/max: ", minval(this%un), maxval(this%un) + end if + print *, "============================" + end subroutine solution_print_info + +end module solution_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/manager/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03a/src/manager/CMakeLists.txt new file mode 100644 index 00000000..00c8bf49 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/manager/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/manager/CMakeLists.txt +message(STATUS "配置管理器模块...") + +# 创建管理器库 +add_library(manager STATIC + component_manager.f90 + component_factory.f90 +) + +# 明确依赖关系:管理器依赖所有其他模块 +target_link_libraries(manager + PRIVATE + core + infrastructure + reconstructor + flux +) + +# 设置模块输出目录 +set_target_properties(manager PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "管理器模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/manager/component_factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/src/manager/component_factory.f90 new file mode 100644 index 00000000..dda16d3e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/manager/component_factory.f90 @@ -0,0 +1,128 @@ +! src/manager/component_factory.f90 (简化版 - 只包含基本功能) +module component_factory_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + use eno_reconstructor_module, only: eno_reconstructor, create_eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor, create_weno3_reconstructor + use rusanov_flux_module, only: rusanov_flux, create_rusanov_flux + + implicit none + private + public :: wp, create_reconstructor, create_flux_calculator + + ! 错误代码 + integer, parameter :: CM_SUCCESS = 0 + integer, parameter :: CM_ERROR_UNKNOWN_SCHEME = 1 + integer, parameter :: CM_ERROR_UNKNOWN_FLUX = 2 + integer, parameter :: CM_ERROR_INVALID_ORDER = 3 + +contains + + ! ==================== 重构器创建 ==================== + + function create_reconstructor(config, status) result(recon) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(reconstructor_base), allocatable :: recon + + character(len=20) :: scheme + integer :: order, error_code + + scheme = trim(adjustl(config%recon_scheme)) + order = config%spatial_order + + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating reconstructor: ", scheme, " order=", order + end if + + select case(scheme) + case('eno') + allocate(eno_reconstructor :: recon) + select type(recon) + type is(eno_reconstructor) + recon = create_eno_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case('weno3') + allocate(weno3_reconstructor :: recon) + select type(recon) + type is(weno3_reconstructor) + recon = create_weno3_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case default + error_code = CM_ERROR_UNKNOWN_SCHEME + if (config%verbose) then + print *, "[ERROR] Unknown reconstructor scheme: ", scheme + print *, " Available: eno, weno3" + end if + end select + + ! 检查阶数有效性 + if (error_code == CM_SUCCESS) then + if (order < 1) then + error_code = CM_ERROR_INVALID_ORDER + if (config%verbose) then + print *, "[ERROR] Invalid spatial order: ", order + end if + end if + end if + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Reconstructor creation failed" + end if + end function create_reconstructor + + ! ==================== 通量计算器创建 ==================== + + function create_flux_calculator(config, status) result(flux) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(flux_calculator_base), allocatable :: flux + + character(len=20) :: flux_type + integer :: error_code + + flux_type = trim(adjustl(config%flux_type)) + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating flux calculator: ", flux_type + end if + + select case(flux_type) + case('rusanov') + allocate(rusanov_flux :: flux) + select type(flux) + type is(rusanov_flux) + flux = create_rusanov_flux() + flux%wave_speed_default = config%wave_speed + end select + + case default + error_code = CM_ERROR_UNKNOWN_FLUX + if (config%verbose) then + print *, "[ERROR] Unknown flux type: ", flux_type + print *, " Available: rusanov" + end if + end select + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Flux calculator creation failed" + end if + end function create_flux_calculator + +end module component_factory_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/manager/component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/src/manager/component_manager.f90 new file mode 100644 index 00000000..e043d99f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/manager/component_manager.f90 @@ -0,0 +1,75 @@ +! src/manager/component_manager.f90 (简化版) +module component_manager_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use component_factory_module, only: create_reconstructor, create_flux_calculator + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + implicit none + private + public :: wp, component_manager_info, validate_config + public :: create_reconstructor, create_flux_calculator + +contains + + ! ==================== 配置验证 ==================== + + function validate_config(config) result(is_valid) + type(cfd_config), intent(in) :: config + logical :: is_valid + + integer :: status + class(reconstructor_base), allocatable :: test_recon + class(flux_calculator_base), allocatable :: test_flux + + is_valid = .false. + + ! 测试创建重构器 + test_recon = create_reconstructor(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid reconstructor configuration" + end if + return + end if + + ! 测试创建通量计算器 + test_flux = create_flux_calculator(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid flux configuration" + end if + return + end if + + ! 清理测试组件 + if (allocated(test_recon)) deallocate(test_recon) + if (allocated(test_flux)) deallocate(test_flux) + + is_valid = .true. + + if (config%verbose) then + print *, "[CONFIG VALIDATION] Configuration is valid" + end if + end function validate_config + + ! ==================== 信息显示 ==================== + + subroutine component_manager_info() + print *, "=== Component Manager ===" + print *, "Available reconstructors:" + print *, " - eno (orders: 1-7)" + print *, " - weno3 (order: 3)" + print *, "" + print *, "Available flux calculators:" + print *, " - rusanov" + print *, "" + print *, "Features:" + print *, " - Configuration validation" + print *, " - Component creation from config" + print *, " - Error handling with status codes" + print *, "=========================" + end subroutine component_manager_info + +end module component_manager_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03a/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03a/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..3fde7746 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,28 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux STATIC + base.f90 + rusanov.f90 +) + +#target_link_libraries(flux PRIVATE core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 明确设置不使用 /Qm64 选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + set_target_properties(flux PROPERTIES + Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /Qm64" # 明确设置,避免继承问题 + ) +endif() + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/src/numerics/flux/base.f90 new file mode 100644 index 00000000..7080a7ae --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/numerics/flux/base.f90 @@ -0,0 +1,30 @@ +!src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + procedure :: print_basic_info => flux_print_basic ! 添加辅助方法 + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + + subroutine flux_print_basic(this) + class(flux_calculator_base), intent(in) :: this + print *, " Name: ", trim(this%name) + end subroutine flux_print_basic + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..daa9e3bb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/numerics/flux/rusanov.f90 @@ -0,0 +1,41 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux, create_rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + + ! 构造函数接口 + interface rusanov_flux + module procedure create_rusanov_flux + end interface + +contains + + ! 构造函数 + type(rusanov_flux) function create_rusanov_flux() result(this) + this%name = "Rusanov" + this%wave_speed_default = 1.0_real64 + end function create_rusanov_flux + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Flux calculator information:" + call this%print_basic_info() + + ! 添加Rusanov特有信息 + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03a/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..5e4b938d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,22 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor STATIC + base.f90 + eno.f90 + weno3.f90 +) + +target_link_libraries(reconstructor PRIVATE core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..53798d02 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/numerics/reconstructor/base.f90 @@ -0,0 +1,33 @@ +!src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + procedure :: print_basic_info => reconstructor_print_basic ! 添加一个辅助方法 + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + + subroutine reconstructor_print_basic(this) + class(reconstructor_base), intent(in) :: this + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_print_basic + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..f973e8b3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor, create_eno_reconstructor ! ← 添加这个 + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + + ! 构造函数接口 + interface eno_reconstructor + module procedure create_eno_reconstructor + end interface + +contains + + ! 构造函数 + type(eno_reconstructor) function create_eno_reconstructor() result(this) + this%name = "ENO" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_eno_reconstructor + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加ENO特有信息 + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..d5b7a747 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno3_reconstructor, create_weno3_reconstructor + + type, extends(reconstructor_base) :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + + ! 构造函数接口 + interface weno3_reconstructor + module procedure create_weno3_reconstructor + end interface + +contains + + ! 构造函数 + type(weno3_reconstructor) function create_weno3_reconstructor() result(this) + this%name = "WENO3" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_weno3_reconstructor + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO3特有信息 + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/physics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03a/src/physics/CMakeLists.txt new file mode 100644 index 00000000..cc4e233a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/physics/CMakeLists.txt @@ -0,0 +1,19 @@ +# src/physics/CMakeLists.txt +message(STATUS "配置物理模块...") + +# 创建物理模块库 +add_library(physics STATIC + physics_interface.f90 + equations/linear_convection.f90 + problems/linear_convection_problem.f90 +) + +# 链接依赖 +target_link_libraries(physics PRIVATE base) + +# 设置模块输出目录 +set_target_properties(physics PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "物理模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/physics/equations/linear_convection.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/src/physics/equations/linear_convection.f90 new file mode 100644 index 00000000..fff7be55 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/physics/equations/linear_convection.f90 @@ -0,0 +1,49 @@ +! src/physics/equations/linear_convection.f90 +module linear_convection_equation + use precision_module, only: wp, ip + use physics_interface, only: physics_equation + implicit none + private + + ! 具体方程类型 - 先声明 + type, extends(physics_equation) :: linear_convection_eq + real(wp) :: wave_speed = 1.0_wp + contains + procedure :: flux => lc_flux + procedure :: speed => lc_speed + end type linear_convection_eq + + ! 公开接口 + public :: wp, ip + public :: linear_convection_eq, create_linear_convection_eq + +contains + + ! 构造函数 + function create_linear_convection_eq(wave_speed) result(eq) + real(wp), intent(in), optional :: wave_speed + type(linear_convection_eq) :: eq + + eq%name = "Linear Convection" + if (present(wave_speed)) then + eq%wave_speed = wave_speed + else + eq%wave_speed = 1.0_wp + end if + end function create_linear_convection_eq + + ! 方法实现 + pure function lc_flux(this, u) result(f) + class(linear_convection_eq), intent(in) :: this + real(wp), intent(in) :: u + real(wp) :: f + f = this%wave_speed * u + end function lc_flux + + pure function lc_speed(this) result(a) + class(linear_convection_eq), intent(in) :: this + real(wp) :: a + a = this%wave_speed + end function lc_speed + +end module linear_convection_equation \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/physics/physics_interface.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/src/physics/physics_interface.f90 new file mode 100644 index 00000000..45002da6 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/physics/physics_interface.f90 @@ -0,0 +1,64 @@ +! src/physics/physics_interface.f90 +module physics_interface + use precision_module, only: wp, ip + implicit none + private + + ! 定义抽象基类型 - 先声明为私有,然后在公开部分导出 + type, abstract :: physics_equation + character(len=:), allocatable :: name + contains + procedure(eq_flux_abs), deferred :: flux + procedure(eq_speed_abs), deferred :: speed + end type physics_equation + + type, abstract :: physics_problem + character(len=:), allocatable :: name + contains + procedure(prob_ic_abs), deferred :: initial_condition + procedure(prob_bc_abs), deferred :: boundary_condition + procedure(prob_exact_abs), deferred :: exact_solution + end type physics_problem + + ! 抽象接口定义 + abstract interface + pure function eq_flux_abs(this, u) result(f) + import :: physics_equation, wp + class(physics_equation), intent(in) :: this + real(wp), intent(in) :: u + real(wp) :: f + end function eq_flux_abs + + pure function eq_speed_abs(this) result(a) + import :: physics_equation, wp + class(physics_equation), intent(in) :: this + real(wp) :: a + end function eq_speed_abs + + subroutine prob_ic_abs(this, x, u) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp), intent(out) :: u(:) + end subroutine prob_ic_abs + + subroutine prob_bc_abs(this, u, t) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(inout) :: u(:) + real(wp), intent(in), optional :: t + end subroutine prob_bc_abs + + function prob_exact_abs(this, x, t) result(u) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(in) :: x(:), t + real(wp), dimension(size(x)) :: u + end function prob_exact_abs + end interface + + ! 公开接口 - 使用独立的public语句 + public :: wp, ip + public :: physics_equation, physics_problem + +end module physics_interface \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/physics/problems/linear_convection_problem.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/src/physics/problems/linear_convection_problem.f90 new file mode 100644 index 00000000..06226ed1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/physics/problems/linear_convection_problem.f90 @@ -0,0 +1,118 @@ +! src/physics/problems/linear_convection_problem.f90 +module linear_convection_problem + use precision_module, only: wp, ip + use physics_interface, only: physics_problem + implicit none + private + + ! 具体问题类型 - 先声明 + type, extends(physics_problem) :: linear_convection_prob + real(wp) :: wave_speed = 1.0_wp + real(wp) :: domain_length = 2.0_wp + character(len=20) :: ic_type = "step" + character(len=20) :: boundary_type = "periodic" + contains + procedure :: initial_condition => lc_initial_condition + procedure :: boundary_condition => lc_boundary_condition + procedure :: exact_solution => lc_exact_solution + end type linear_convection_prob + + ! 公开接口 + public :: wp, ip + public :: linear_convection_prob, create_linear_convection_prob + +contains + + ! 构造函数 + function create_linear_convection_prob(wave_speed, domain_length, & + ic_type, boundary_type) result(prob) + real(wp), intent(in), optional :: wave_speed, domain_length + character(len=*), intent(in), optional :: ic_type, boundary_type + type(linear_convection_prob) :: prob + + prob%name = "Linear Convection Problem" + + if (present(wave_speed)) prob%wave_speed = wave_speed + if (present(domain_length)) prob%domain_length = domain_length + if (present(ic_type)) prob%ic_type = ic_type + if (present(boundary_type)) prob%boundary_type = boundary_type + end function create_linear_convection_prob + + ! 初始条件 + subroutine lc_initial_condition(this, x, u) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp), intent(out) :: u(:) + + integer :: i + + select case (trim(this%ic_type)) + case ("step") + do i = 1, size(x) + if (x(i) >= 0.5_wp .and. x(i) <= 1.0_wp) then + u(i) = 2.0_wp + else + u(i) = 1.0_wp + end if + end do + + case ("sin", "sine") + do i = 1, size(x) + u(i) = sin(2.0_wp * 3.141592653589793_wp * x(i) / this%domain_length) + end do + + case ("gaussian") + do i = 1, size(x) + u(i) = exp(-((x(i) - 0.5_wp) / 0.1_wp)**2) + end do + + case default + ! 默认阶跃函数 + do i = 1, size(x) + if (x(i) >= 0.5_wp .and. x(i) <= 1.0_wp) then + u(i) = 2.0_wp + else + u(i) = 1.0_wp + end if + end do + end select + end subroutine lc_initial_condition + + ! 边界条件(虚拟实现,实际在boundary模块) + subroutine lc_boundary_condition(this, u, t) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(inout) :: u(:) + real(wp), intent(in), optional :: t + + ! 边界条件将在独立模块实现 + print *, "[PROBLEM] Boundary condition placeholder" + if (present(t)) then + print *, " Time = ", t + end if + end subroutine lc_boundary_condition + + ! 精确解(周期性平移) + function lc_exact_solution(this, x, t) result(u) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(in) :: x(:), t + real(wp), dimension(size(x)) :: u + real(wp), dimension(size(x)) :: x_shifted + integer :: i + + ! 周期性平移 + do i = 1, size(x) + x_shifted(i) = x(i) - this%wave_speed * t + ! 确保在 [0, domain_length) 范围内 + do while (x_shifted(i) < 0.0_wp) + x_shifted(i) = x_shifted(i) + this%domain_length + end do + do while (x_shifted(i) >= this%domain_length) + x_shifted(i) = x_shifted(i) - this%domain_length + end do + end do + + ! 重用初始条件函数 + call this%initial_condition(x_shifted, u) + end function lc_exact_solution + +end module linear_convection_problem \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/solver/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03a/src/solver/CMakeLists.txt new file mode 100644 index 00000000..3f320bca --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/solver/CMakeLists.txt @@ -0,0 +1,18 @@ +# src/solver/CMakeLists.txt +message(STATUS "配置求解器模块...") + +add_library(solver STATIC + base.f90 +) + +target_link_libraries(solver + PRIVATE + infrastructure + core +) + +set_target_properties(solver PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "求解器模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/src/solver/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/src/solver/base.f90 new file mode 100644 index 00000000..1881ba08 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/src/solver/base.f90 @@ -0,0 +1,258 @@ +! src/solver/base.f90 +module solver_base_module + use base_modules, only: wp => wp, ip => ip ! 重命名以避免冲突 + + use config_module, only: cfd_config + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create + + implicit none + private + + ! 明确导出列表 + public :: wp, ip ! 类型参数 + public :: solver_base, create_solver_base ! 类型和构造函数 + public :: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, SOLVER_RUNNING + public :: SOLVER_COMPLETED, SOLVER_ERROR ! 状态常量 + + ! 求解器状态枚举 + integer, parameter :: SOLVER_UNINITIALIZED = 0 + integer, parameter :: SOLVER_INITIALIZED = 1 + integer, parameter :: SOLVER_RUNNING = 2 + integer, parameter :: SOLVER_COMPLETED = 3 + integer, parameter :: SOLVER_ERROR = 4 + + ! 求解器基类 + type :: solver_base + ! 基本组件 + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + + ! 状态管理 + integer :: state = SOLVER_UNINITIALIZED + character(len=100) :: error_message = "" + real(wp) :: current_time = 0.0_wp + integer(ip) :: current_step = 0 + + ! 时间控制 + real(wp) :: dt_original = 0.0_wp + contains + procedure :: initialize => solver_base_initialize + procedure :: step => solver_base_step + procedure :: run_to_time => solver_base_run_to_time + procedure :: cleanup => solver_base_cleanup + procedure :: get_state => solver_base_get_state + procedure :: get_error => solver_base_get_error + procedure :: print_info => solver_base_print_info + end type solver_base + + ! 构造函数接口 + interface solver_base + module procedure create_solver_base + end interface + +contains + + ! ==================== 构造函数 ==================== + + function create_solver_base(config, mesh) result(solver) + type(cfd_config), intent(in) :: config + type(mesh_type), intent(in) :: mesh + type(solver_base) :: solver + + solver%config = config + solver%mesh = mesh + + ! 创建域 + solver%domain = domain_create(config, mesh) + + ! 创建解 + solver%solution = solution_create(solver%domain) + + ! 保存原始时间步长 + solver%dt_original = config%dt + + if (config%verbose) then + print *, "[SOLVER] Base solver created" + print *, " Mesh cells: ", mesh%ncells + print *, " Domain total cells: ", solver%domain%ntcells + end if + end function create_solver_base + + ! ==================== 初始化 ==================== + + subroutine solver_base_initialize(this) + class(solver_base), intent(inout) :: this + + if (this%state == SOLVER_INITIALIZED) then + if (this%config%verbose) then + print *, "[SOLVER] Already initialized" + end if + return + end if + + ! 初始化解(通过配置) + ! 这里暂时简化,实际需要调用初始条件工厂 + print *, "[INFO] Base solver initialized (simplified)" + + ! 更新状态 + this%state = SOLVER_INITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + + if (this%config%verbose) then + print *, "[SOLVER] Initialized at t = ", this%current_time + end if + end subroutine solver_base_initialize + + ! ==================== 单步计算(虚方法) ==================== + + subroutine solver_base_step(this, dt) + class(solver_base), intent(inout) :: this + real(wp), intent(in) :: dt + + ! 基类中这只是虚方法,需要在子类中实现 + print *, "[INFO] Base solver step (virtual method)" + print *, " dt = ", dt + print *, " t = ", this%current_time + + ! 更新时间 + this%current_time = this%current_time + dt + this%current_step = this%current_step + 1 + + ! 简单模拟:只是更新状态 + if (this%config%verbose) then + print *, "[SOLVER] Step completed: t = ", this%current_time, & + ", step = ", this%current_step + end if + end subroutine solver_base_step + + ! ==================== 运行到指定时间 ==================== + + subroutine solver_base_run_to_time(this, final_time) + class(solver_base), intent(inout) :: this + real(wp), intent(in) :: final_time + + real(wp) :: dt, t_remaining + integer :: step_count + + if (this%state /= SOLVER_INITIALIZED) then + this%error_message = "Solver not initialized" + this%state = SOLVER_ERROR + return + end if + + this%state = SOLVER_RUNNING + step_count = 0 + + if (this%config%verbose) then + print *, "[SOLVER] Running from t = ", this%current_time, & + " to t = ", final_time + end if + + do while (this%current_time < final_time) + ! 计算时间步长 + dt = min(this%config%dt, final_time - this%current_time) + + ! 执行时间步 + call this%step(dt) + + step_count = step_count + 1 + + ! 每10步输出一次进度 + if (mod(step_count, 10) == 0 .and. this%config%verbose) then + print *, "[SOLVER] Progress: t = ", this%current_time, & + " / ", final_time + end if + end do + + ! 恢复原始时间步长 + this%config%dt = this%dt_original + + ! 更新状态 + this%state = SOLVER_COMPLETED + + if (this%config%verbose) then + print *, "[SOLVER] Run completed:" + print *, " Final time: ", this%current_time + print *, " Total steps: ", this%current_step + end if + end subroutine solver_base_run_to_time + + ! ==================== 清理 ==================== + + subroutine solver_base_cleanup(this) + class(solver_base), intent(inout) :: this + + ! 重置状态 + this%state = SOLVER_UNINITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + this%error_message = "" + + if (this%config%verbose) then + print *, "[SOLVER] Cleaned up" + end if + end subroutine solver_base_cleanup + + ! ==================== 状态查询 ==================== + + function solver_base_get_state(this) result(state) + class(solver_base), intent(in) :: this + integer :: state + state = this%state + end function solver_base_get_state + + function solver_base_get_error(this) result(error_msg) + class(solver_base), intent(in) :: this + character(len=100) :: error_msg + error_msg = trim(this%error_message) + end function solver_base_get_error + + ! ==================== 信息打印 ==================== + + subroutine solver_base_print_info(this) + class(solver_base), intent(in) :: this + + character(len=20) :: state_str + + ! 状态字符串 + select case (this%state) + case (SOLVER_UNINITIALIZED) + state_str = "Uninitialized" + case (SOLVER_INITIALIZED) + state_str = "Initialized" + case (SOLVER_RUNNING) + state_str = "Running" + case (SOLVER_COMPLETED) + state_str = "Completed" + case (SOLVER_ERROR) + state_str = "Error" + case default + state_str = "Unknown" + end select + + print *, "=== Solver Information ===" + print *, "State: ", trim(state_str) + print *, "Current time: ", this%current_time + print *, "Current step: ", this%current_step + print *, "Error message: '", trim(this%error_message), "'" + + ! 配置信息 + print *, "Configuration:" + print *, " Scheme: ", trim(this%config%recon_scheme) + print *, " Order: ", this%config%spatial_order + print *, " dt: ", this%config%dt + + ! 域信息 + print *, "Domain:" + print *, " Ghost layers: ", this%domain%nghosts + print *, " Physical cells: ", this%domain%ist, " to ", this%domain%ied - 1 + + print *, "=========================" + end subroutine solver_base_print_info + +end module solver_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/CMakeLists.txt new file mode 100644 index 00000000..bb152a95 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/CMakeLists.txt @@ -0,0 +1,83 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +# +#message(STATUS "CMAKE_Fortran_MODULE_DIRECTORY=${CMAKE_Fortran_MODULE_DIRECTORY}") +# +add_executable(test_minimal_simple test_minimal_simple.f90) +target_link_libraries(test_minimal_simple + PRIVATE + core # 必须链接core库 + infrastructure +) + + +add_executable(test_simple_link test_simple_link.f90) +target_link_libraries(test_simple_link + PRIVATE + reconstructor + flux +) + + +add_executable(test_factory_simple test_factory_simple.f90) +target_link_libraries(test_factory_simple + PRIVATE + core + infrastructure + reconstructor + flux +) + + +add_executable(test_component_manager test_component_manager.f90) + +target_link_libraries(test_component_manager + PRIVATE + manager # ← 链接到新的管理器库 + infrastructure +) +add_executable(test_basic_only test_basic_only.f90) +target_link_libraries(test_basic_only + PRIVATE + infrastructure + core +) + +add_executable(test_physics_minimal test_physics_minimal.f90) +target_link_libraries(test_physics_minimal + PRIVATE + physics + base +) + +add_executable(test_domain_solution test_domain_solution.f90) +target_link_libraries(test_domain_solution + PRIVATE + infrastructure + core +) + +add_executable(test_config_physics test_config_physics.f90) +target_link_libraries(test_config_physics + PRIVATE + infrastructure + core +) + +add_executable(test_solver_base test_solver_base.f90) +target_link_libraries(test_solver_base + PRIVATE + solver + infrastructure + core +) + +add_executable(test_component_manager_physics test_component_manager_physics.f90) +target_link_libraries(test_component_manager_physics + PRIVATE + manager + infrastructure + physics + core +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_basic_only.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_basic_only.f90 new file mode 100644 index 00000000..20901ddc --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_basic_only.f90 @@ -0,0 +1,56 @@ +! tests/test_basic_only.f90 +program test_basic_only + ! 只测试最基本的功能,不依赖复杂模块 + use config_module, only: cfd_config, config_print, wp + use mesh_module, only: mesh_type + use registry_module, only: registry_init, registry_cleanup, & + register_component_simple, list_components + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "=== BASIC TEST - Minimal Functionality ===" + print *, "" + + ! 测试1: 配置 + print *, "1. Testing configuration..." + print *, "----------------------------" + call config_print(config) + print *, "" + + ! 测试2: 网格 + print *, "2. Testing mesh..." + print *, "------------------" + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=5) + print *, "Mesh initialized:" + print *, " Cells: ", mesh%ncells + print *, " Nodes: ", mesh%nnodes + print *, " dx: ", mesh%dx + print *, "" + + ! 测试3: 注册系统 + print *, "3. Testing registry..." + print *, "----------------------" + + call registry_init() + + ! 注册组件(使用简化版本) + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! 列出组件 + call list_components() + print *, "" + + ! 清理 + call registry_cleanup() + + print *, "=== TEST PASSED ===" + print *, "✓ Configuration works" + print *, "✓ Mesh works" + print *, "✓ Registry works" + +end program test_basic_only \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_cfd_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_cfd_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_cfd_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_component_manager.f90 new file mode 100644 index 00000000..f60c3505 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_component_manager.f90 @@ -0,0 +1,111 @@ +! tests/test_component_manager.f90 +program test_component_manager + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use component_manager_module, only: create_reconstructor, create_flux_calculator + use component_manager_module, only: component_manager_info, validate_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + implicit none + + type(cfd_config) :: config + class(reconstructor_base), allocatable :: recon + class(flux_calculator_base), allocatable :: flux + integer :: status + logical :: is_valid + + print *, "=== Component Manager Test ===" + print *, "" + + ! 显示组件管理器信息 + call component_manager_info() + print *, "" + + ! 测试1: 基本配置 + print *, "1. Testing basic ENO3 + Rusanov configuration..." + print *, "-----------------------------------------------" + + config%verbose = .true. + call config_print(config) + + ! 配置ENO3重构 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + call config_print(config) + print *, "" + + ! 验证配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Configuration is valid" + else + print *, "[ERROR] Configuration is invalid" + end if + print *, "" + + ! 测试2: 创建组件 + print *, "2. Testing component creation..." + print *, "--------------------------------" + + ! 创建重构器(带状态检查) + recon = create_reconstructor(config, status) + if (status == 0) then + print *, "[OK] Reconstructor created successfully" + call recon%info() + else + print *, "[ERROR] Failed to create reconstructor, code:", status + end if + print *, "" + + ! 创建通量计算器 + flux = create_flux_calculator(config, status) + if (status == 0) then + print *, "[OK] Flux calculator created successfully" + call flux%info() + else + print *, "[ERROR] Failed to create flux calculator, code:", status + end if + print *, "" + + ! 测试3: WENO3重构测试 + print *, "3. Testing WENO3 configuration..." + print *, "---------------------------------" + + call config_with_reconstruction(config, "weno3", 3) + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] WENO3 configuration is valid" + + ! 创建WENO3重构器 + recon = create_reconstructor(config) + call recon%info() + else + print *, "[ERROR] WENO3 configuration is invalid" + end if + print *, "" + + ! 测试4: 错误配置测试 + print *, "4. Testing invalid configuration..." + print *, "-----------------------------------" + + config%recon_scheme = "unknown_scheme" + config%flux_type = "unknown_flux" + + is_valid = validate_config(config) + if (.not. is_valid) then + print *, "[OK] Invalid configuration correctly rejected" + else + print *, "[ERROR] Invalid configuration should have been rejected" + end if + + ! 清理 + if (allocated(recon)) deallocate(recon) + if (allocated(flux)) deallocate(flux) + + print *, "" + print *, "=== Component manager test completed successfully ===" + +end program test_component_manager \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_component_manager_physics.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_component_manager_physics.f90 new file mode 100644 index 00000000..f2becca9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_component_manager_physics.f90 @@ -0,0 +1,120 @@ +! tests/test_component_manager_physics.f90 (简化版) +program test_component_manager_physics + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + use component_manager_module, only: component_manager_info, validate_config + + implicit none + + type(cfd_config) :: config + logical :: is_valid + + print *, "=== Component Manager Physics Test (Simplified) ===" + print *, "" + + ! 测试1: 显示组件管理器信息 + print *, "1. Testing component manager info..." + print *, "-------------------------------------" + call component_manager_info() + print *, "" + + ! 测试2: 物理模块测试(默认) + print *, "2. Testing physics module with default configuration..." + print *, "------------------------------------------------------" + + config%verbose = .true. + call config_print(config) + + ! 验证配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Default configuration is valid" + else + print *, "[ERROR] Default configuration is invalid" + end if + print *, "" + + ! 测试3: 测试物理配置 + print *, "3. Testing physics configuration..." + print *, "------------------------------------" + + ! 修改物理参数 + config%equation_type = "linear_advection" + config%problem_type = "linear_advection" + config%wave_speed = 2.5_wp + config%domain_length = 3.0_wp + + print *, "Modified physics configuration:" + print *, " Equation type: ", trim(config%equation_type) + print *, " Problem type: ", trim(config%problem_type) + print *, " Wave speed: ", config%wave_speed + print *, " Domain length: ", config%domain_length + print *, " Physics enabled: ", config%enable_physics + + ! 验证修改后的配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Modified physics configuration is valid" + else + print *, "[ERROR] Modified physics configuration is invalid" + end if + print *, "" + + ! 测试4: 数值组件测试 + print *, "4. Testing numerical components with physics..." + print *, "-----------------------------------------------" + + call config_with_reconstruction(config, "weno3", 3) + config%flux_type = "rusanov" + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Combined physics+numerics configuration is valid" + else + print *, "[ERROR] Combined configuration is invalid" + end if + print *, "" + + ! 测试5: 物理模块禁用测试 + print *, "5. Testing physics module disabled..." + print *, "---------------------------------------" + + config%enable_physics = .false. + config%verbose = .false. + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Configuration valid even with physics disabled" + else + print *, "[ERROR] Configuration should be valid with physics disabled" + end if + print *, "" + + ! 测试6: 错误配置测试 + print *, "6. Testing error handling..." + print *, "-----------------------------" + + config%verbose = .true. + config%enable_physics = .true. + config%recon_scheme = "unknown_scheme" + config%flux_type = "unknown_flux" + config%equation_type = "unknown_equation" + config%problem_type = "unknown_problem" + + is_valid = validate_config(config) + if (.not. is_valid) then + print *, "[OK] Invalid configuration correctly rejected" + else + print *, "[ERROR] Invalid configuration should have been rejected" + end if + print *, "" + + print *, "=== Component Manager Physics Test Summary ===" + print *, "✓ Component manager info works" + print *, "✓ Configuration validation works with physics" + print *, "✓ Error handling works correctly" + print *, "✓ Combined physics+numerics validation works" + print *, "" + print *, "下一步: 集成物理模块到求解器框架" + +end program test_component_manager_physics \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_config_physics.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_config_physics.f90 new file mode 100644 index 00000000..c6fef5c0 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_config_physics.f90 @@ -0,0 +1,141 @@ +! tests/test_config_physics.f90 (修复版) +program test_config_physics + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + + implicit none + + type(cfd_config) :: config + + print *, "=== Configuration Physics Test (Simplified) ===" + print *, "" + + ! 测试1: 默认配置 + print *, "1. Testing default configuration..." + print *, "-----------------------------------" + call config_print(config) + print *, "" + + ! 测试2: 验证基础物理字段 + print *, "2. Testing basic physics fields..." + print *, "----------------------------------" + + print *, "Verifying default physics fields:" + + if (trim(config%equation_type) == "linear_advection") then + print *, " ✓ Default equation type: linear_advection" + else + print *, " ✗ Unexpected equation type: ", trim(config%equation_type) + end if + + if (trim(config%problem_type) == "linear_advection") then + print *, " ✓ Default problem type: linear_advection" + else + print *, " ✗ Unexpected problem type: ", trim(config%problem_type) + end if + + if (abs(config%domain_length - 2.0_wp) < 1e-10_wp) then + print *, " ✓ Default domain length: 2.0" + else + print *, " ✗ Unexpected domain length: ", config%domain_length + end if + + if (config%enable_physics) then + print *, " ✓ Physics enabled by default" + else + print *, " ✗ Physics not enabled by default" + end if + + print *, "" + + ! 测试3: 使用类型绑定的方法(正确的方法名) + print *, "3. Testing type-bound procedures..." + print *, "--------------------------------------" + + call config%set_physics_parameters( & + equation_type="burgers_equation", & + problem_type="sod_shock_tube", & + domain_length=3.0_wp, & + enable_physics=.false.) + + print *, "After set_physics_parameters:" + print *, " Equation type: ", trim(config%equation_type) + print *, " Problem type: ", trim(config%problem_type) + print *, " Domain length: ", config%domain_length + print *, " Physics enabled: ", config%enable_physics + + if (trim(config%equation_type) == "burgers_equation") then + print *, " ✓ Equation type modified successfully via set_physics_parameters" + end if + + if (trim(config%problem_type) == "sod_shock_tube") then + print *, " ✓ Problem type modified successfully via set_physics_parameters" + end if + + if (abs(config%domain_length - 3.0_wp) < 1e-10_wp) then + print *, " ✓ Domain length modified successfully via set_physics_parameters" + end if + + if (.not. config%enable_physics) then + print *, " ✓ Physics disabled successfully via set_physics_parameters" + end if + + print *, "" + + ! 测试4: 调用get_physics_info方法 + print *, "4. Testing get_physics_info method..." + print *, "--------------------------------------" + call config%get_physics_info() + print *, "" + + ! 测试5: 高斯脉冲配置 + print *, "5. Testing Gaussian pulse configuration..." + print *, "-----------------------------------------" + + config%ic_type = "gaussian" + config%pulse_center = 0.6_wp + config%pulse_width = 0.15_wp + + print *, "Gaussian pulse parameters:" + print *, " IC type: ", trim(config%ic_type) + print *, " Center: ", config%pulse_center + print *, " Width: ", config%pulse_width + + if (trim(config%ic_type) == "gaussian") then + print *, " ✓ Gaussian IC type set" + end if + + if (abs(config%pulse_center - 0.6_wp) < 1e-10_wp) then + print *, " ✓ Pulse center set" + end if + + if (abs(config%pulse_width - 0.15_wp) < 1e-10_wp) then + print *, " ✓ Pulse width set" + end if + + print *, "" + + ! 测试6: 重构配置 + print *, "6. Testing reconstruction configuration..." + print *, "------------------------------------------" + + call config_with_reconstruction(config, "weno", 5) + + print *, "Reconstruction configuration:" + print *, " Scheme: ", trim(config%recon_scheme) + print *, " Order: ", config%spatial_order + + if (trim(config%recon_scheme) == "weno" .and. config%spatial_order == 5) then + print *, " ✓ WENO5 configuration successful" + else + print *, " ✗ Reconstruction configuration failed" + end if + + print *, "" + + print *, "=== Configuration Physics Test Complete ===" + print *, "✓ Config module updated with physics support" + print *, "✓ Fields can be directly accessed and modified" + print *, "✓ Type-bound procedures work correctly" + +end program test_config_physics \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_domain_solution.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_domain_solution.f90 new file mode 100644 index 00000000..ff659bac --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_domain_solution.f90 @@ -0,0 +1,102 @@ +! tests/test_domain_solution.f90 +program test_domain_solution + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create, solution_reset + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + real(wp), allocatable :: initial_values(:) + integer :: i + + print *, "=== Domain and Solution Test ===" + print *, "" + + ! 测试1: 不同重构方案的ghost层计算 + print *, "1. Testing ghost layer calculation..." + print *, "--------------------------------------" + + ! ENO3 + call config_with_reconstruction(config, "eno", 3) + config%verbose = .false. + call mesh%init(ncells=10) + domain = domain_create(config, mesh) + print *, "ENO3: nghosts = ", domain%nghosts, " (expected: 3)" + + ! WENO3 + call config_with_reconstruction(config, "weno3", 3) + domain = domain_create(config, mesh) + print *, "WENO3: nghosts = ", domain%nghosts, " (expected: 2)" + + ! WENO5 + call config_with_reconstruction(config, "weno", 5) + domain = domain_create(config, mesh) + print *, "WENO5: nghosts = ", domain%nghosts, " (expected: 3)" + print *, "" + + ! 测试2: Solution数组 + print *, "2. Testing solution arrays..." + print *, "------------------------------" + + call config_with_reconstruction(config, "eno", 3) + config%verbose = .true. + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + domain = domain_create(config, mesh) + call domain%print_info() + print *, "" + + solution = solution_create(domain) + call solution%print_info() + print *, "" + + ! 测试3: 初始化和更新 + print *, "3. Testing initialization and update..." + print *, "----------------------------------------" + + allocate(initial_values(mesh%ncells)) + do i = 1, mesh%ncells + initial_values(i) = sin(2.0_wp * 3.14159265358979_wp * mesh%xcc(i) / mesh%L) + end do + + call solution%initialize(initial_values) + print *, "After initialization:" + print *, " u range: ", minval(solution%u), " to ", maxval(solution%u) + print *, " un range: ", minval(solution%un), " to ", maxval(solution%un) + + ! 修改当前解,测试更新 + solution%u = solution%u * 2.0_wp + call solution%update_old_field() + print *, "After update: max|u - un| = ", maxval(abs(solution%u - solution%un)) + print *, "" + + ! 测试4: 重置 + print *, "4. Testing reset..." + print *, "-------------------" + + call solution_reset(solution) + print *, "After reset:" + print *, " u max: ", maxval(abs(solution%u)) + print *, " un max: ", maxval(abs(solution%un)) + print *, " flux max: ", maxval(abs(solution%flux)) + print *, "" + + deallocate(initial_values) + + print *, "=== Test Summary ===" + print *, "✓ Ghost layer calculation works" + print *, "✓ Domain creation works" + print *, "✓ Solution arrays work" + print *, "✓ Initialization works" + print *, "✓ Field update works" + print *, "✓ Reset works" + print *, "" + print *, "Ready for next step: Implementing Physics modules" + +end program test_domain_solution \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_factory_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_factory_simple.f90 new file mode 100644 index 00000000..db65da7c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_factory_simple.f90 @@ -0,0 +1,58 @@ +! tests/test_factory_simple.f90 (修复版) +program test_factory_simple + use base_modules, only: wp ! ← 添加这行 + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Basic systems + print *, "1. Testing basic systems..." + print *, "-----------------------------" + call config_print(config) + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Creating reconstructors + print *, "2. Testing reconstructors..." + print *, "------------------------------" + + ! 创建并测试ENO重构器 + print *, "Creating ENO reconstructor..." + eno = eno_reconstructor() ! 使用构造函数 + call eno%info() ! 必须调用info方法 + + print *, "" + print *, "Creating WENO3 reconstructor..." + weno3 = weno3_reconstructor() ! 使用构造函数 + call weno3%info() ! 必须调用info方法 + print *, "" + + ! Test 3: Creating flux calculator + print *, "3. Testing flux calculator..." + print *, "-------------------------------" + + print *, "Creating Rusanov flux calculator..." + rusanov = rusanov_flux() ! 使用构造函数 + call rusanov%info() ! 必须调用info方法 + print *, "" + + print *, "=== Factory pattern simple test completed successfully ===" + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_minimal_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_minimal_simple.f90 new file mode 100644 index 00000000..ec03ccf8 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_minimal_simple.f90 @@ -0,0 +1,87 @@ +! tests/test_minimal_simple.f90 +program test_minimal_simple + use base_modules, only: wp ! ← 添加这行 + use registry_module + use config_module + use mesh_module + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Simple Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call registry_init() + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call list_components() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + + if (has_component_simple("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component_simple("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Test getting available components (simple version) + print *, "5. Testing registry functionality" + print *, "---------------------------------" + print *, "Registry initialized: ", registry_is_initialized() + print *, "Number of components: ", registry_get_size() + print *, "" + + ! Cleanup + call registry_cleanup() + + print *, "=== Simple test completed successfully ===" + +end program test_minimal_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_physics_minimal.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_physics_minimal.f90 new file mode 100644 index 00000000..cf2a28f1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_physics_minimal.f90 @@ -0,0 +1,91 @@ +! tests/test_physics_minimal.f90 +program test_physics_minimal + use precision_module, only: wp, ip + use linear_convection_equation, only: linear_convection_eq, create_linear_convection_eq + use linear_convection_problem, only: linear_convection_prob, create_linear_convection_prob + + implicit none + + type(linear_convection_eq) :: eq + type(linear_convection_prob) :: prob + real(wp) :: u, f, a + real(wp), allocatable :: x(:), u_ic(:), u_exact(:) + integer :: i, nx = 10 + + print *, "=== 最小物理模块测试 ===" + print *, "" + + ! 测试1: 方程功能 + print *, "1. 测试方程功能..." + print *, "-------------------" + + eq = create_linear_convection_eq(wave_speed=2.0_wp) + print *, "方程: ", eq%name + print *, "波速: ", eq%wave_speed + + u = 1.5_wp + f = eq%flux(u) + a = eq%speed() + + print *, "u = ", u + print *, "F(u) = ", f, " (期望: 3.0)" + print *, "波速 a = ", a, " (期望: 2.0)" + + if (abs(f - 3.0_wp) < 1e-10_wp .and. abs(a - 2.0_wp) < 1e-10_wp) then + print *, "✓ 方程功能正常" + else + print *, "✗ 方程功能异常" + end if + print *, "" + + ! 测试2: 问题功能 + print *, "2. 测试问题功能..." + print *, "-------------------" + + prob = create_linear_convection_prob(ic_type="step", domain_length=2.0_wp) + print *, "问题: ", prob%name + print *, "IC类型: ", trim(prob%ic_type) + print *, "域长度: ", prob%domain_length + + allocate(x(nx), u_ic(nx), u_exact(nx)) + do i = 1, nx + x(i) = 0.0_wp + (i-1) * 0.2_wp + end do + + ! 测试初始条件 + call prob%initial_condition(x, u_ic) + print *, "初始条件范围: ", minval(u_ic), " 到 ", maxval(u_ic) + + ! 测试精确解 + u_exact = prob%exact_solution(x, 0.0_wp) + print *, "t=0时精确解范围: ", minval(u_exact), " 到 ", maxval(u_exact) + + ! 检查阶跃函数 + if (abs(u_ic(1) - 1.0_wp) < 1e-10_wp .and. & + abs(u_ic(6) - 2.0_wp) < 1e-10_wp) then + print *, "✓ 阶跃初始条件正确" + else + print *, "✗ 阶跃初始条件错误" + end if + + ! 检查精确解与初始条件一致 + if (maxval(abs(u_ic - u_exact)) < 1e-10_wp) then + print *, "✓ t=0时精确解与初始条件一致" + else + print *, "✗ 精确解计算错误" + end if + print *, "" + + ! 测试3: 边界条件接口 + print *, "3. 测试边界条件接口..." + print *, "----------------------" + call prob%boundary_condition(u_ic, 0.0_wp) + print *, "✓ 边界条件接口正常" + print *, "" + + deallocate(x, u_ic, u_exact) + + print *, "=== 物理模块最小测试完成 ===" + print *, "下一步: 将物理模块集成到现有系统中" + +end program test_physics_minimal \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_simple_link.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_simple_link.f90 new file mode 100644 index 00000000..71cc614e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_simple_link.f90 @@ -0,0 +1,78 @@ +! tests/test_simple_link.f90 +program test_simple_link + use base_modules, only: wp ! ← 添加这行 + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call registry_init() + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call list_components() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component_simple("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component_simple("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Cleanup + call registry_cleanup() + + print *, "=== Minimal test completed successfully ===" + +end program test_simple_link \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_solver_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_solver_base.f90 new file mode 100644 index 00000000..6cfe47e4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_solver_base.f90 @@ -0,0 +1,99 @@ +! tests/test_solver_base.f90 (修复版) +program test_solver_base + ! 所有 USE 语句必须在程序开始处 + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + use mesh_module, only: mesh_type + use solver_base_module, only: solver_base, SOLVER_UNINITIALIZED, & + SOLVER_INITIALIZED, SOLVER_COMPLETED, SOLVER_ERROR + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(solver_base) :: solver + integer :: state + + print *, "=== Solver Base Test ===" + print *, "" + + ! 测试1: 创建求解器 + print *, "1. Creating solver..." + print *, "----------------------" + + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%dt = 0.01_wp + + call config_print(config) + + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + solver = solver_base(config, mesh) + call solver%print_info() + print *, "" + + ! 测试2: 初始化 + print *, "2. Initializing solver..." + print *, "-------------------------" + + call solver%initialize() + state = solver%get_state() + print *, "State after initialization: ", state + print *, "Expected: ", SOLVER_INITIALIZED + print *, "Match? ", state == SOLVER_INITIALIZED + print *, "Error message: '", trim(solver%get_error()), "'" + print *, "" + + ! 测试3: 运行求解器 + print *, "3. Running solver..." + print *, "--------------------" + + call solver%run_to_time(0.05_wp) + state = solver%get_state() + print *, "State after run: ", state + print *, "Expected: ", SOLVER_COMPLETED + print *, "Match? ", state == SOLVER_COMPLETED + print *, "Current time: ", solver%current_time + print *, "Current step: ", solver%current_step + print *, "" + + ! 测试4: 再次运行(从已完成状态) + print *, "4. Running again from completed state..." + print *, "----------------------------------------" + + ! 需要先清理才能重新运行 + call solver%cleanup() + call solver%initialize() + call solver%run_to_time(0.1_wp) + + call solver%print_info() + print *, "" + + ! 测试5: 错误处理 + print *, "5. Testing error states..." + print *, "--------------------------" + + ! 创建一个未初始化的求解器 + call solver%cleanup() + state = solver%get_state() + print *, "Uninitialized state: ", state + print *, "Expected: ", SOLVER_UNINITIALIZED + print *, "Match? ", state == SOLVER_UNINITIALIZED + + ! 尝试运行未初始化的求解器 + call solver%run_to_time(0.01_wp) + state = solver%get_state() + print *, "State after error: ", state + print *, "Expected: ", SOLVER_ERROR + print *, "Match? ", state == SOLVER_ERROR + print *, "Error message: '", trim(solver%get_error()), "'" + print *, "" + + print *, "=== Solver Base Test Complete ===" + print *, "✓ Solver base class works" + print *, "✓ State management works" + print *, "✓ Time stepping framework works" + print *, "✓ Error handling works" + +end program test_solver_base \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_solver_framework.f90 b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_solver_framework.f90 new file mode 100644 index 00000000..6754323d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03a/tests/test_solver_framework.f90 @@ -0,0 +1,91 @@ +! tests/test_solver_framework.f90 +program test_solver_framework + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use mesh_module, only: mesh_type + use solver_module, only: cfd_solver, solver_create, solver_run, solver_cleanup + use solver_module, only: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, & + SOLVER_COMPLETED, SOLVER_ERROR + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(cfd_solver) :: solver + + print *, "=== 求解器框架测试 ===" + print *, "" + + ! 测试1: 基本创建 + print *, "1. 测试求解器创建..." + print *, "----------------------" + + ! 创建配置 + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.0_real64 + config%dt = 0.01_real64 + + call config_print(config) + print *, "" + + ! 创建网格 + call mesh%init(xmin=0.0_real64, xmax=2.0_real64, ncells=20) + call mesh%print_info() + print *, "" + + ! 创建求解器 + solver = solver_create(config, mesh) + print *, "✓ 求解器创建成功" + print *, " 状态: ", solver%get_state() + print *, "" + + ! 测试2: 求解器初始化 + print *, "2. 测试求解器初始化..." + print *, "------------------------" + + call solver%initialize() + print *, "✓ 求解器初始化完成" + print *, " 状态: ", solver%get_state() + print *, " 错误信息: '", trim(solver%get_error()), "'" + print *, "" + + ! 测试3: 简单运行 + print *, "3. 测试求解器运行..." + print *, "----------------------" + + call solver_run(solver, 0.05_real64) ! 运行到0.05秒 + print *, "✓ 求解器运行完成" + print *, " 最终状态: ", solver%get_state() + print *, "" + + ! 测试4: 清理 + print *, "4. 测试求解器清理..." + print *, "----------------------" + + call solver_cleanup(solver) + print *, "✓ 求解器清理完成" + print *, " 状态: ", solver%get_state() + print *, "" + + ! 测试5: 错误处理 + print *, "5. 测试错误处理..." + print *, "-------------------" + + ! 尝试重复初始化 + call solver%initialize() + print *, " 重复初始化状态: ", solver%get_state() + print *, " 错误信息: '", trim(solver%get_error()), "'" + + call solver_cleanup(solver) + print *, "" + + print *, "=== 框架测试总结 ===" + print *, "✓ 求解器创建/初始化/运行/清理流程验证完成" + print *, "✓ 状态管理正常工作" + print *, "✓ 错误处理机制就绪" + print *, "" + print *, "下一步: 添加实际数值计算功能" + +end program test_solver_framework \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03b/CMakeLists.txt new file mode 100644 index 00000000..55859dc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) +add_subdirectory(examples) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/README.md b/example/1d-linear-convection/weno3/fortran/registry/03b/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/examples/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03b/examples/CMakeLists.txt new file mode 100644 index 00000000..9207e0f4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/examples/CMakeLists.txt @@ -0,0 +1,22 @@ +# examples/CMakeLists.txt +message(STATUS "配置示例程序...") + +# 主示例程序:ENO/WENO对比 +add_executable(example_eno_weno_comparison + run_eno_weno.f90 +) + +target_link_libraries(example_eno_weno_comparison + PRIVATE + solver + infrastructure + core + physics + manager +) + +install(TARGETS example_eno_weno_comparison + RUNTIME DESTINATION bin/examples +) + +message(STATUS "示例程序配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/examples/run_eno_weno.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/examples/run_eno_weno.f90 new file mode 100644 index 00000000..e96e50b0 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/examples/run_eno_weno.f90 @@ -0,0 +1,318 @@ +! examples/run_eno_weno.f90 +program run_eno_weno + ! 示例程序:ENO/WENO对比分析 + ! 对应Julia版本的功能 + + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction, config_print + use mesh_module, only: mesh_type + use registry_module, only: registry_init, registry_cleanup, initialize_default_components + use solver_base_module, only: SOLVER_INITIALIZED, SOLVER_COMPLETED, SOLVER_ERROR + use physics_solver_module, only: physics_solver + + implicit none + + ! 求解器实例 + type(cfd_config) :: config_eno3, config_weno3, config_weno5 + type(mesh_type) :: mesh + type(physics_solver) :: solver_eno3, solver_weno3, solver_weno5 + + character(len=100) :: status_str + integer :: step_count + logical :: all_success ! 在声明部分声明变量 + + print *, "==========================================" + print *, " ENO/WENO对比分析 - Fortran版本" + print *, " 对应Julia examples/run_eno_weno.jl" + print *, "==========================================" + print *, "" + + ! 步骤0: 系统初始化 + print *, "[步骤0] 初始化系统..." + print *, "---------------------" + + ! 初始化注册系统 + call registry_init(verbose=.true.) + + ! 注册默认组件(关键:必须注册才能创建) + print *, "注册默认组件..." + call initialize_default_components() + print *, "" + + ! 步骤1: 创建网格 + print *, "[步骤1] 创建计算网格..." + print *, "------------------------" + + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=40) + call mesh%print_info() + print *, "" + + ! ========== ENO3 求解器 ========== + print *, "[步骤2] 配置并运行ENO3求解器..." + print *, "--------------------------------" + + ! 配置ENO3 + config_eno3%verbose = .true. + config_eno3%ic_type = "step" + config_eno3%wave_speed = 1.0_wp + config_eno3%final_time = 0.625_wp + config_eno3%dt = 0.0025_wp + config_eno3%rk_order = 2 + config_eno3%boundary_type = "periodic" + config_eno3%equation_type = "linear_advection" + config_eno3%problem_type = "linear_advection" + config_eno3%enable_physics = .true. + + call config_with_reconstruction(config_eno3, "eno", 3) + + print *, "ENO3配置信息:" + call config_print(config_eno3) + print *, "" + + ! 创建并运行ENO3求解器 + print *, "创建ENO3求解器实例..." + solver_eno3 = physics_solver(config_eno3, mesh) + + print *, "初始化ENO3求解器..." + call solver_eno3%initialize() + + print *, "运行ENO3求解器..." + step_count = 0 + call solver_eno3%run_to_time(config_eno3%final_time) + + ! 检查ENO3状态 + print *, "检查ENO3求解器状态..." + if (solver_eno3%get_state() == SOLVER_COMPLETED) then + print *, "✓ ENO3求解器运行完成" + print *, " 最终时间: ", solver_eno3%current_time + print *, " 总步数: ", solver_eno3%current_step + print *, " 错误信息: '", trim(solver_eno3%get_error()), "'" + else if (solver_eno3%get_state() == SOLVER_ERROR) then + print *, "✗ ENO3求解器运行失败" + print *, " 错误信息: ", trim(solver_eno3%get_error()) + else + print *, "? ENO3求解器状态: ", solver_eno3%get_state() + end if + print *, "" + + ! ========== WENO3 求解器 ========== + print *, "[步骤3] 配置并运行WENO3求解器..." + print *, "---------------------------------" + + ! 配置WENO3 + config_weno3%verbose = .true. + config_weno3%ic_type = "step" + config_weno3%wave_speed = 1.0_wp + config_weno3%final_time = 0.625_wp + config_weno3%dt = 0.0025_wp + config_weno3%rk_order = 2 + config_weno3%boundary_type = "periodic" + config_weno3%equation_type = "linear_advection" + config_weno3%problem_type = "linear_advection" + config_weno3%enable_physics = .true. + + call config_with_reconstruction(config_weno3, "weno3", 3) + + print *, "WENO3配置信息:" + call config_print(config_weno3) + print *, "" + + ! 创建并运行WENO3求解器 + print *, "创建WENO3求解器实例..." + solver_weno3 = physics_solver(config_weno3, mesh) + + print *, "初始化WENO3求解器..." + call solver_weno3%initialize() + + print *, "运行WENO3求解器..." + call solver_weno3%run_to_time(config_weno3%final_time) + + ! 检查WENO3状态 + print *, "检查WENO3求解器状态..." + if (solver_weno3%get_state() == SOLVER_COMPLETED) then + print *, "✓ WENO3求解器运行完成" + print *, " 最终时间: ", solver_weno3%current_time + print *, " 总步数: ", solver_weno3%current_step + print *, " 错误信息: '", trim(solver_weno3%get_error()), "'" + else if (solver_weno3%get_state() == SOLVER_ERROR) then + print *, "✗ WENO3求解器运行失败" + print *, " 错误信息: ", trim(solver_weno3%get_error()) + else + print *, "? WENO3求解器状态: ", solver_weno3%get_state() + end if + print *, "" + + ! ========== WENO5 求解器 ========== + print *, "[步骤4] 配置并运行WENO5求解器..." + print *, "---------------------------------" + + ! 配置WENO5 + config_weno5%verbose = .true. + config_weno5%ic_type = "step" + config_weno5%wave_speed = 1.0_wp + config_weno5%final_time = 0.625_wp + config_weno5%dt = 0.0025_wp + config_weno5%rk_order = 2 + config_weno5%boundary_type = "periodic" + config_weno5%equation_type = "linear_advection" + config_weno5%problem_type = "linear_advection" + config_weno5%enable_physics = .true. + + call config_with_reconstruction(config_weno5, "weno", 5) + + print *, "WENO5配置信息:" + call config_print(config_weno5) + print *, "" + + ! 创建并运行WENO5求解器 + print *, "创建WENO5求解器实例..." + solver_weno5 = physics_solver(config_weno5, mesh) + + print *, "初始化WENO5求解器..." + call solver_weno5%initialize() + + print *, "运行WENO5求解器..." + call solver_weno5%run_to_time(config_weno5%final_time) + + ! 检查WENO5状态 + print *, "检查WENO5求解器状态..." + if (solver_weno5%get_state() == SOLVER_COMPLETED) then + print *, "✓ WENO5求解器运行完成" + print *, " 最终时间: ", solver_weno5%current_time + print *, " 总步数: ", solver_weno5%current_step + print *, " 错误信息: '", trim(solver_weno5%get_error()), "'" + else if (solver_weno5%get_state() == SOLVER_ERROR) then + print *, "✗ WENO5求解器运行失败" + print *, " 错误信息: ", trim(solver_weno5%get_error()) + else + print *, "? WENO5求解器状态: ", solver_weno5%get_state() + end if + print *, "" + + ! ========== 结果汇总 ========== + print *, "[步骤5] 结果汇总..." + print *, "--------------------" + print *, "" + + print *, "求解器状态对比:" + print *, "---------------" + + ! ENO3状态 + if (solver_eno3%get_state() == SOLVER_COMPLETED) then + status_str = "完成 ✓" + else if (solver_eno3%get_state() == SOLVER_INITIALIZED) then + status_str = "已初始化" + else if (solver_eno3%get_state() == SOLVER_ERROR) then + status_str = "错误 ✗" + else + status_str = "未知状态" + end if + print *, "ENO3: ", trim(status_str) + print *, " 最终时间: ", solver_eno3%current_time + print *, " 总步数: ", solver_eno3%current_step + print *, "" + + ! WENO3状态 + if (solver_weno3%get_state() == SOLVER_COMPLETED) then + status_str = "完成 ✓" + else if (solver_weno3%get_state() == SOLVER_INITIALIZED) then + status_str = "已初始化" + else if (solver_weno3%get_state() == SOLVER_ERROR) then + status_str = "错误 ✗" + else + status_str = "未知状态" + end if + print *, "WENO3: ", trim(status_str) + print *, " 最终时间: ", solver_weno3%current_time + print *, " 总步数: ", solver_weno3%current_step + print *, "" + + ! WENO5状态 + if (solver_weno5%get_state() == SOLVER_COMPLETED) then + status_str = "完成 ✓" + else if (solver_weno5%get_state() == SOLVER_INITIALIZED) then + status_str = "已初始化" + else if (solver_weno5%get_state() == SOLVER_ERROR) then + status_str = "错误 ✗" + else + status_str = "未知状态" + end if + print *, "WENO5: ", trim(status_str) + print *, " 最终时间: ", solver_weno5%current_time + print *, " 总步数: ", solver_weno5%current_step + print *, "" + + ! ========== 清理 ========== + print *, "[步骤6] 系统清理..." + print *, "-------------------" + + call solver_eno3%cleanup() + call solver_weno3%cleanup() + call solver_weno5%cleanup() + call registry_cleanup() + + print *, "" + print *, "==========================================" + print *, " 分析完成" + print *, "==========================================" + print *, "" + + ! 总结所有求解器状态 + all_success = (solver_eno3%get_state() == SOLVER_COMPLETED) .and. & + (solver_weno3%get_state() == SOLVER_COMPLETED) .and. & + (solver_weno5%get_state() == SOLVER_COMPLETED) + + if (all_success) then + print *, "✓ 所有求解器成功运行!" + print *, "" + print *, "计算参数总结:" + print *, "--------------" + print *, "网格单元数: ", mesh%ncells + print *, "时间步长: ", config_eno3%dt + print *, "最终时间: ", config_eno3%final_time + print *, "波速: ", config_eno3%wave_speed + print *, "边界条件: ", trim(config_eno3%boundary_type) + print *, "初始条件: ", trim(config_eno3%ic_type) + print *, "" + print *, "重构格式对比:" + print *, "--------------" + print *, "ENO3: 3阶本质无振荡" + print *, "WENO3: 3阶加权本质无振荡" + print *, "WENO5: 5阶加权本质无振荡" + print *, "" + print *, "下一步开发计划:" + print *, "1. 添加边界条件模块实现" + print *, "2. 完善ENO/WENO重构算法" + print *, "3. 实现Rusanov通量计算" + print *, "4. 添加RK时间积分器" + print *, "5. 实现结果输出和可视化" + else + print *, "✗ 有求解器运行失败" + print *, "" + print *, "故障排除:" + print *, "----------" + + if (solver_eno3%get_state() /= SOLVER_COMPLETED) then + print *, "• ENO3失败: ", trim(solver_eno3%get_error()) + end if + + if (solver_weno3%get_state() /= SOLVER_COMPLETED) then + print *, "• WENO3失败: ", trim(solver_weno3%get_error()) + end if + + if (solver_weno5%get_state() /= SOLVER_COMPLETED) then + print *, "• WENO5失败: ", trim(solver_weno5%get_error()) + end if + + print *, "" + print *, "可能的原因:" + print *, "1. 组件注册不完整" + print *, "2. 重构器工厂配置错误" + print *, "3. 物理模块初始化失败" + print *, "4. 内存分配问题" + end if + + print *, "" + print *, "==========================================" + +end program run_eno_weno \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/build.bat new file mode 100644 index 00000000..6fd6dc03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/build.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Project Builder +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python build script with full Intel environment support... +echo. + +python build.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Build failed + pause + exit /b 1 +) + +echo. +echo [INFO] Build completed successfully! +echo. +echo [INFO] To run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/build.py new file mode 100644 index 00000000..3bf6d537 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/build.py @@ -0,0 +1,629 @@ +#!/usr/bin/env python3 +""" +Fortran CFD Project Builder - 完整Python解决方案 +在Python内部处理Intel oneAPI环境配置 +""" + +import os +import sys +import subprocess +import shutil +import argparse +import time +import platform +import tempfile +from pathlib import Path + +class IntelEnvironment: + """Intel oneAPI环境管理器""" + + def __init__(self): + self.setvars_path = None + self.env_vars = {} + + def find_setvars(self): + """查找setvars.bat文件""" + possible_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + r"C:\Program Files (x86)\Intel\oneAPI\compiler\latest\env\vars.bat", + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\setvars.bat"), + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\compiler\latest\env\vars.bat"), + ] + + for path in possible_paths: + if os.path.exists(path): + self.setvars_path = path + return True + + return False + + def setup_environment(self): + """设置Intel环境""" + if not self.find_setvars(): + return False + + try: + # 创建临时的批处理文件来捕获环境变量 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + f.write(f'@echo off\n') + f.write(f'call "{self.setvars_path}" >nul 2>&1\n') + f.write(f'set\n') # 输出所有环境变量 + temp_bat = f.name + + # 运行批处理文件并捕获输出 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + shell=True + ) + + # 解析环境变量 + for line in result.stdout.split('\n'): + line = line.strip() + if '=' in line: + key, value = line.split('=', 1) + self.env_vars[key.strip()] = value.strip() + + # 清理临时文件 + os.unlink(temp_bat) + + # 更新当前进程的环境变量 + os.environ.update(self.env_vars) + + return True + + except Exception as e: + print(f"设置Intel环境失败: {e}") + return False + + def get_compiler_info(self): + """获取编译器信息""" + info = {} + + # 检查ifx编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + env={**os.environ, **self.env_vars} if self.env_vars else os.environ + ) + + if result.returncode == 0: + for line in result.stdout.split('\n'): + if 'Version' in line or '版本' in line: + info['ifx_version'] = line.strip() + break + except: + pass + + # 检查环境变量 + info['ifx_root'] = self.env_vars.get('IFX_ROOT', '') + info['compiler_root'] = self.env_vars.get('ONEAPI_ROOT', '') + + return info + +class BuildSystem: + """构建系统主类""" + + def __init__(self): + self.project_root = Path(__file__).parent.parent + self.build_dir = self.project_root / "build" + self.intel_env = IntelEnvironment() + + # 设置控制台编码 + if sys.platform == "win32": + try: + import ctypes + # 设置控制台输出为UTF-8 + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + except: + pass + + def print_header(self, text): + """打印标题""" + print(f"\n{'='*70}") + print(f" {text}") + print(f"{'='*70}\n") + + def print_step(self, step, total, message): + """打印步骤""" + print(f"[{step}/{total}] {message}...") + + def print_success(self, message): + """打印成功""" + print(f"\033[92m✓ {message}\033[0m") + + def print_error(self, message): + """打印错误""" + print(f"\033[91m✗ {message}\033[0m") + + def print_warning(self, message): + """打印警告""" + print(f"\033[93m! {message}\033[0m") + + def print_info(self, message): + """打印信息""" + print(f"\033[94mℹ {message}\033[0m") + + def check_prerequisites(self): + """检查前提条件""" + self.print_step(1, 6, "检查前提条件") + + # 检查Python版本 + python_version = sys.version.split()[0] + self.print_info(f"Python版本: {python_version}") + + # 检查平台 + self.print_info(f"平台: {platform.system()} {platform.release()}") + self.print_info(f"处理器核心数: {os.cpu_count()}") + + # 检查CMake + try: + result = subprocess.run( + ["cmake", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + version_line = result.stdout.split('\n')[0] + self.print_success(f"CMake: {version_line}") + else: + self.print_error("CMake未找到") + return False + except FileNotFoundError: + self.print_error("CMake未安装") + return False + + return True + + def setup_intel_environment(self, args): + """设置Intel环境""" + self.print_step(2, 6, "配置Intel oneAPI环境") + + if not self.intel_env.find_setvars(): + self.print_warning("未找到Intel oneAPI setvars.bat") + self.print_info("将尝试使用系统环境中的编译器") + + # 检查是否能直接访问编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + self.print_success("Intel编译器在系统PATH中找到") + return True + else: + self.print_warning("Intel编译器未在PATH中找到") + except: + self.print_warning("无法访问Intel编译器") + + return True # 继续,让CMake自己找编译器 + + # 设置环境 + if self.intel_env.setup_environment(): + compiler_info = self.intel_env.get_compiler_info() + + if compiler_info.get('ifx_version'): + self.print_success(f"Intel Fortran编译器: {compiler_info['ifx_version']}") + elif compiler_info.get('ifx_root'): + self.print_success(f"Intel编译器路径: {compiler_info['ifx_root']}") + else: + self.print_success("Intel oneAPI环境配置完成") + + return True + else: + self.print_warning("Intel环境配置失败,将继续使用系统环境") + return True + + def clean_build_directory(self, args): + """清理构建目录""" + if args.clean and self.build_dir.exists(): + self.print_info("清理构建目录...") + try: + shutil.rmtree(self.build_dir) + self.print_success("构建目录已清理") + except Exception as e: + self.print_error(f"清理失败: {e}") + if not args.force: + return False + return True + + def run_command(self, cmd, cwd=None, check=True, env=None): + """运行命令""" + if isinstance(cmd, list): + cmd_str = ' '.join(str(c) for c in cmd if c) + else: + cmd_str = str(cmd) + + print(f" \033[96m$\033[0m {cmd_str}") + + try: + # 合并环境变量 + exec_env = os.environ.copy() + if env: + exec_env.update(env) + if self.intel_env.env_vars: + exec_env.update(self.intel_env.env_vars) + + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=False, + env=exec_env + ) + + # 处理输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or '完成' in line or '生成' in line: + print(f" \033[92m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + if check and result.returncode != 0: + self.print_error(f"命令执行失败,退出码: {result.returncode}") + return False + + return True + + except Exception as e: + self.print_error(f"命令执行异常: {e}") + return False + + def configure_cmake(self, args): + """配置CMake""" + self.print_step(3, 6, "配置CMake项目") + + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ] + + if args.compiler == "ifx": + cmake_cmd.extend(["-T", "fortran=ifx"]) + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + success = self.run_command(cmake_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("CMake配置完成") + else: + self.print_error("CMake配置失败") + + return success + + def build_project(self, args): + """构建项目""" + self.print_step(4, 6, "构建项目") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + success = self.run_command(build_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("项目构建完成") + else: + self.print_error("构建失败") + + return success + + def run_tests_with_environment(self, test_exe): + """运行单个测试,确保有Intel环境""" + try: + # 创建临时的批处理文件来运行测试 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'"{test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'"{test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + return result + + except Exception as e: + print(f"运行测试失败: {e}") + return None + + def run_tests(self, args): + """运行测试""" + self.print_step(5, 6, "运行测试") + + # 查找测试可执行文件 + test_dir = self.build_dir / "bin" / args.build_type + if not test_dir.exists(): + test_dir = self.build_dir / "bin" + if not test_dir.exists(): + test_dir = self.build_dir + + test_files = list(test_dir.glob("test_*.exe")) + + if not test_files: + self.print_warning("未找到测试程序") + return True + + all_passed = True + + for test_exe in sorted(test_files): + test_name = test_exe.stem + self.print_info(f"运行测试: {test_name}") + print(f" {'-'*50}") + + # 运行测试 + result = self.run_tests_with_environment(str(test_exe)) + + if result is None: + self.print_error(f" {test_name} 运行失败") + all_passed = False + continue + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + print(f" {line}") + + if result.returncode == 0: + self.print_success(f" {test_name} 通过") + else: + self.print_error(f" {test_name} 失败 (退出码: {result.returncode})") + all_passed = False + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + print() # 空行 + + return all_passed + + def create_test_runner(self, args): + """创建独立的测试运行器""" + self.print_step(6, 6, "创建测试运行器") + + runner_path = self.build_dir / "run_tests.bat" + + content = f'''@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Test Runner +echo ======================================== +echo. + +REM Setup Intel oneAPI environment +set "SETVARS_PATH={self.intel_env.setvars_path or ''}" +if exist "%SETVARS_PATH%" ( + call "%SETVARS_PATH%" >nul + echo [INFO] Intel environment configured +) else ( + echo [WARNING] Intel environment not found + echo [WARNING] Tests may fail without runtime libraries +) + +echo. + +REM Run all test executables +set "TEST_COUNT=0" +set "PASS_COUNT=0" + +for %%f in ("bin\\{args.build_type}\\test_*.exe") do ( + set /a TEST_COUNT+=1 + echo [TEST %%f] + echo {'-'*50} + + %%f + if errorlevel 1 ( + echo [FAILED] %%f + ) else ( + echo [PASSED] %%f + set /a PASS_COUNT+=1 + ) + echo. +) + +echo ======================================== +echo Tests: %PASS_COUNT%/%TEST_COUNT% passed +if %PASS_COUNT% equ %TEST_COUNT% ( + echo [SUCCESS] All tests passed! +) else ( + echo [FAILURE] Some tests failed +) +echo ======================================== + +pause +''' + + with open(runner_path, 'w', encoding='utf-8') as f: + f.write(content) + + self.print_success(f"测试运行器已创建: {runner_path}") + self.print_info(f"使用方法: cd build && run_tests.bat") + + return runner_path + + def generate_report(self, args, build_time, tests_passed): + """生成构建报告""" + self.print_header("构建完成") + + print(f"项目: {self.project_root.name}") + print(f"构建类型: {args.build_type}") + print(f"编译器: {args.compiler}") + print(f"并行作业: {args.jobs}") + print(f"总耗时: {build_time:.1f}秒") + print(f"测试结果: {'全部通过' if tests_passed else '有失败'}") + + # 显示生成的可执行文件 + bin_dir = self.build_dir / "bin" / args.build_type + if bin_dir.exists(): + print(f"\n生成的可执行文件:") + for exe in sorted(bin_dir.glob("*.exe")): + size_mb = exe.stat().st_size / (1024 * 1024) + print(f" • {exe.name} ({size_mb:.2f} MB)") + + # 显示测试运行器信息 + runner_path = self.build_dir / "run_tests.bat" + if runner_path.exists(): + print(f"\n独立测试运行器:") + print(f" • {runner_path.name}") + print(f" 在Intel oneAPI环境中运行所有测试") + + def run(self): + """运行构建系统""" + parser = argparse.ArgumentParser( + description="Fortran CFD项目构建工具", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认构建 + %(prog)s --clean # 清理后构建 + %(prog)s --build-type Release # Release构建 + %(prog)s --no-tests # 只构建,不运行测试 + %(prog)s -j8 --verbose # 8线程并行构建,详细输出 + """ + ) + + parser.add_argument("--build-type", choices=["Debug", "Release"], + default="Debug", help="构建类型") + parser.add_argument("--compiler", choices=["ifx", "ifort"], + default="ifx", help="Fortran编译器") + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-tests", action="store_true", + help="跳过测试") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + + args = parser.parse_args() + + # 开始构建 + start_time = time.time() + + self.print_header("Fortran CFD 项目构建系统") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 检查前提条件 + if not self.check_prerequisites(): + if not args.force: + return 1 + + # 2. 设置Intel环境 + if not self.setup_intel_environment(args): + if not args.force: + return 1 + + # 3. 清理目录 + if not self.clean_build_directory(args): + if not args.force: + return 1 + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 4. 配置CMake + if not self.configure_cmake(args): + if not args.force: + return 1 + + # 5. 构建项目 + if not self.build_project(args): + if not args.force: + return 1 + + # 6. 运行测试和创建测试运行器 + tests_passed = True + if not args.no_tests: + tests_passed = self.run_tests(args) + + # 创建测试运行器 + self.create_test_runner(args) # 传递 args 参数 + + # 7. 生成报告 + build_time = time.time() - start_time + self.generate_report(args, build_time, tests_passed) + + return 0 if tests_passed else 1 + + except KeyboardInterrupt: + self.print_error("\n构建被用户中断") + return 1 + except Exception as e: + self.print_error(f"构建过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + builder = BuildSystem() + return builder.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/run_all_steps.bat b/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/run_all_steps.bat new file mode 100644 index 00000000..d506149b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/run_all_steps.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo CFD Project: All Steps +echo ======================================== +echo. + +echo [INFO] Starting Step 1: Physics Modules Test... +call run_step1.bat + +if errorlevel 1 ( + echo [ERROR] Step 1 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Starting Step 2: Configuration Physics Update... +call run_step2.bat + +if errorlevel 1 ( + echo [ERROR] Step 2 failed + pause + exit /b 1 +) + +echo. +echo ======================================== +echo All Steps Completed Successfully! +echo ======================================== +echo. +echo [INFO] Next: Update component manager for physics support +echo [INFO] Run: run_step3.bat (to be created) +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/run_example.py b/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/run_example.py new file mode 100644 index 00000000..d7c19917 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/run_example.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +# scripts/run_example.py +""" +运行ENO/WENO示例程序 +""" + +import os +import sys +import subprocess +from pathlib import Path + +# 添加当前目录到路径 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import BuildSystem +except ImportError: + print("错误: 找不到build.py,请确保在scripts目录中运行") + sys.exit(1) + +def run_example(): + """运行示例程序""" + builder = BuildSystem() + + print("\n" + "="*70) + print(" 运行ENO/WENO对比示例程序") + print("="*70 + "\n") + + # 检查是否已构建 + exe_path = builder.build_dir / "bin" / "Debug" / "example_eno_weno_comparison.exe" + + if not exe_path.exists(): + print("示例程序未构建,先构建项目...") + print("-"*50) + + # 使用简化的构建 + result = subprocess.run( + ["python", "build.py", "--no-tests", "--clean"], + cwd=builder.project_root / "scripts", + capture_output=True, + text=True + ) + + if result.returncode != 0: + print("构建失败:") + print(result.stderr) + return False + + # 运行示例程序 + print("运行示例程序...") + print("-"*50) + + try: + result = subprocess.run( + [str(exe_path)], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace' + ) + + print(result.stdout) + + if result.stderr: + print("标准错误输出:") + print(result.stderr) + + return result.returncode == 0 + + except Exception as e: + print(f"运行示例程序失败: {e}") + return False + +def main(): + """主函数""" + success = run_example() + + if success: + print("\n" + "="*70) + print(" ✓ 示例程序运行成功") + print("="*70) + return 0 + else: + print("\n" + "="*70) + print(" ✗ 示例程序运行失败") + print("="*70) + return 1 + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/run_step1.bat b/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/run_step1.bat new file mode 100644 index 00000000..0b6b1f17 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/run_step1.bat @@ -0,0 +1,39 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Step 1: Physics Modules Test +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python step1 script with full Intel environment support... +echo. + +python run_step1.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Step 1 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Step 1 completed successfully! +echo. +echo [INFO] Next step: Update config to include physics settings +echo [INFO] Run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/run_step1.py b/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/run_step1.py new file mode 100644 index 00000000..5e087a69 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/run_step1.py @@ -0,0 +1,319 @@ +#!/usr/bin/env python3 +""" +Step 1: Physics Modules Test +扩展build.py,专门用于测试物理模块 +""" + +import os +import sys +import subprocess +import time +from pathlib import Path + +# 添加当前目录到路径,以便导入build.py的类 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import IntelEnvironment, BuildSystem +except ImportError: + print("Error: Cannot import build.py. Make sure build.py is in the same directory.") + sys.exit(1) + +class Step1System(BuildSystem): + """Step 1 测试系统,继承自BuildSystem""" + + def __init__(self): + super().__init__() + self.test_name = "test_physics_minimal" + self.test_exe = None + + def find_test_executable(self): + """查找测试可执行文件""" + possible_paths = [ + self.build_dir / "bin" / "Debug" / f"{self.test_name}.exe", + self.build_dir / "Debug" / f"{self.test_name}.exe", + self.build_dir / f"{self.test_name}.exe", + self.build_dir / "bin" / f"{self.test_name}.exe", + ] + + for path in possible_paths: + if path.exists(): + self.test_exe = path + self.print_success(f"Found test executable: {path}") + return True + + # 如果没有找到,尝试搜索 + self.print_warning(f"Could not find {self.test_name}.exe") + self.print_info("Searching for test executables...") + + try: + result = subprocess.run( + ["dir", str(self.build_dir), "/s", "/b", "*.exe"], + capture_output=True, + text=True, + encoding='utf-8', + shell=True + ) + + if result.returncode == 0: + test_files = [line.strip() for line in result.stdout.split('\n') + if line and 'test_' in line.lower()] + + if test_files: + self.print_info("Found test files:") + for test_file in test_files: + self.print_info(f" {test_file}") + return False + except: + pass + + return False + + def run_test_with_intel_env(self): + """在Intel环境下运行测试""" + if not self.test_exe: + self.print_error("No test executable found") + return False + + self.print_step(1, 2, f"Running test: {self.test_exe.name}") + + try: + # 创建临时的批处理文件来运行测试(包含Intel环境) + import tempfile + + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + # 设置Intel环境 + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'echo [INFO] Intel environment configured\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'echo [WARNING] Intel environment not found\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + self.print_info(f"Command: {temp_bat}") + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower() or 'fail' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or 'pass' in line.lower() or '✓' in line: + print(f" \033[92m{line}\033[0m") + elif '=' in line or '---' in line: + print(f" \033[96m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + self.print_warning("Test stderr output:") + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + self.print_step(2, 2, "Test execution completed") + + if result.returncode == 0: + self.print_success("Test passed") + return True + else: + self.print_error(f"Test failed (exit code: {result.returncode})") + return False + + except Exception as e: + self.print_error(f"Failed to run test: {e}") + return False + + def build_project_if_needed(self, args): + """如果需要,构建项目""" + if args.no_build: + self.print_info("Skipping build (--no-build flag)") + return True + + self.print_step(1, 3, "Building project") + + # 调用父类的构建方法 + build_args = argparse.Namespace() + build_args.clean = args.clean + build_args.build_type = "Debug" + build_args.compiler = "ifx" + build_args.no_tests = True # 不运行所有测试 + build_args.jobs = os.cpu_count() + build_args.verbose = args.verbose + build_args.force = args.force + + # 清理构建目录 + if args.clean and self.build_dir.exists(): + self.print_info("Cleaning build directory...") + import shutil + try: + shutil.rmtree(self.build_dir) + self.print_success("Build directory cleaned") + except Exception as e: + self.print_error(f"Clean failed: {e}") + if not args.force: + return False + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 配置CMake + self.print_step(2, 3, "Configuring CMake") + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-DCMAKE_BUILD_TYPE=Debug", + "-T", "fortran=ifx", + ] + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + if not self.run_command(cmake_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + # 构建项目 + self.print_step(3, 3, "Building project") + build_cmd = [ + "cmake", + "--build", ".", + "--config", "Debug", + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + if not self.run_command(build_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + self.print_success("Build completed") + return True + + def run(self): + """运行Step 1测试""" + parser = argparse.ArgumentParser( + description="Step 1: Physics Modules Test", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认运行 + %(prog)s --clean # 清理后构建并测试 + %(prog)s --no-build # 只运行测试,不重新构建 + %(prog)s --verbose # 详细输出 + %(prog)s -j4 # 使用4个并行作业构建 + """ + ) + + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-build", action="store_true", + help="不重新构建,直接运行测试") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + + args = parser.parse_args() + + # 开始测试 + start_time = time.time() + + self.print_header("Step 1: Physics Modules Implementation Test") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 设置Intel环境 + self.print_step(1, 4, "Setting up Intel oneAPI environment") + if not self.setup_intel_environment(args): + if not args.force: + self.print_error("Intel environment setup failed") + return 1 + self.print_warning("Intel environment setup failed, continuing...") + + # 2. 构建项目(如果需要) + self.print_step(2, 4, "Building project if needed") + if not self.build_project_if_needed(args): + if not args.force: + return 1 + + # 3. 查找测试可执行文件 + self.print_step(3, 4, "Finding test executable") + if not self.find_test_executable(): + if not args.force: + return 1 + self.print_warning("Test executable not found, but continuing due to --force") + return 0 + + # 4. 运行测试 + self.print_step(4, 4, "Running physics module test") + test_passed = self.run_test_with_intel_env() + + # 生成报告 + test_time = time.time() - start_time + self.print_header("Step 1 Complete") + + print(f"测试: {'通过 ✓' if test_passed else '失败 ✗'}") + print(f"测试程序: {self.test_exe.name if self.test_exe else '未找到'}") + print(f"总耗时: {test_time:.1f}秒") + + if test_passed: + print(f"\n下一步: 更新配置以包含物理设置") + print(f"建议: 修改config.f90,添加physics相关字段") + return 0 + else: + if args.force: + self.print_warning("测试失败,但由于--force标志继续执行") + return 0 + return 1 + + except KeyboardInterrupt: + self.print_error("\n测试被用户中断") + return 1 + except Exception as e: + self.print_error(f"测试过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + system = Step1System() + return system.run() + +if __name__ == "__main__": + # 需要导入argparse + import argparse + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/run_step2.bat b/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/run_step2.bat new file mode 100644 index 00000000..9c1f62de --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/run_step2.bat @@ -0,0 +1,39 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Step 2: Configuration Physics Update +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python step2 script with full Intel environment support... +echo. + +python run_step2.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Step 2 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Step 2 completed successfully! +echo. +echo [INFO] Next step: Update component manager to support physics +echo [INFO] Run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/run_step2.py b/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/run_step2.py new file mode 100644 index 00000000..c16b7608 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/scripts/run_step2.py @@ -0,0 +1,284 @@ +#!/usr/bin/env python3 +""" +Step 2: Configuration Physics Update +测试配置模块的物理功能更新 +""" + +import os +import sys +import subprocess +import time +from pathlib import Path + +# 添加当前目录到路径,以便导入build.py的类 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import IntelEnvironment, BuildSystem +except ImportError: + print("Error: Cannot import build.py. Make sure build.py is in the same directory.") + sys.exit(1) + +class Step2System(BuildSystem): + """Step 2 测试系统,继承自BuildSystem""" + + def __init__(self): + super().__init__() + self.test_name = "test_config_physics" + self.test_exe = None + + def find_test_executable(self): + """查找测试可执行文件""" + possible_paths = [ + self.build_dir / "bin" / "Debug" / f"{self.test_name}.exe", + self.build_dir / "Debug" / f"{self.test_name}.exe", + self.build_dir / f"{self.test_name}.exe", + self.build_dir / "bin" / f"{self.test_name}.exe", + ] + + for path in possible_paths: + if path.exists(): + self.test_exe = path + self.print_success(f"Found test executable: {path}") + return True + + return False + + def run_test_with_intel_env(self): + """在Intel环境下运行测试""" + if not self.test_exe: + self.print_error("No test executable found") + return False + + self.print_step(1, 2, f"Running test: {self.test_exe.name}") + + try: + # 创建临时的批处理文件来运行测试(包含Intel环境) + import tempfile + + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + # 设置Intel环境 + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'echo [INFO] Intel environment configured\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'echo [WARNING] Intel environment not found\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + self.print_info(f"Command: {temp_bat}") + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower() or 'fail' in line.lower() or '✗' in line: + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or 'pass' in line.lower() or '✓' in line: + print(f" \033[92m{line}\033[0m") + elif '=' in line or '---' in line or '===' in line: + print(f" \033[96m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + self.print_warning("Test stderr output:") + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + self.print_step(2, 2, "Test execution completed") + + if result.returncode == 0: + self.print_success("Test passed") + return True + else: + self.print_error(f"Test failed (exit code: {result.returncode})") + return False + + except Exception as e: + self.print_error(f"Failed to run test: {e}") + return False + + def build_project(self, args): + """构建项目""" + if args.no_build: + self.print_info("Skipping build (--no-build flag)") + return True + + self.print_step(1, 3, "Building project") + + # 清理构建目录 + if args.clean and self.build_dir.exists(): + self.print_info("Cleaning build directory...") + import shutil + try: + shutil.rmtree(self.build_dir) + self.print_success("Build directory cleaned") + except Exception as e: + self.print_error(f"Clean failed: {e}") + if not args.force: + return False + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 配置CMake + self.print_step(2, 3, "Configuring CMake") + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-DCMAKE_BUILD_TYPE=Debug", + "-T", "fortran=ifx", + ] + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + if not self.run_command(cmake_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + # 构建项目 + self.print_step(3, 3, "Building project") + build_cmd = [ + "cmake", + "--build", ".", + "--config", "Debug", + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + if not self.run_command(build_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + self.print_success("Build completed") + return True + + def run(self): + """运行Step 2测试""" + import argparse + + parser = argparse.ArgumentParser( + description="Step 2: Configuration Physics Update", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认运行 + %(prog)s --clean # 清理后构建并测试 + %(prog)s --no-build # 只运行测试,不重新构建 + %(prog)s --verbose # 详细输出 + """ + ) + + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-build", action="store_true", + help="不重新构建,直接运行测试") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + + args = parser.parse_args() + + # 开始测试 + start_time = time.time() + + self.print_header("Step 2: Configuration Physics Update") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 设置Intel环境 + self.print_step(1, 4, "Setting up Intel oneAPI environment") + if not self.setup_intel_environment(args): + if not args.force: + self.print_error("Intel environment setup failed") + return 1 + self.print_warning("Intel environment setup failed, continuing...") + + # 2. 构建项目(如果需要) + self.print_step(2, 4, "Building project if needed") + if not self.build_project(args): + if not args.force: + return 1 + + # 3. 查找测试可执行文件 + self.print_step(3, 4, "Finding test executable") + if not self.find_test_executable(): + self.print_error(f"Test executable {self.test_name}.exe not found") + if not args.force: + return 1 + self.print_warning("Test executable not found, but continuing due to --force") + return 0 + + # 4. 运行测试 + self.print_step(4, 4, "Running configuration physics test") + test_passed = self.run_test_with_intel_env() + + # 生成报告 + test_time = time.time() - start_time + self.print_header("Step 2 Complete") + + print(f"测试: {'通过 ✓' if test_passed else '失败 ✗'}") + print(f"测试程序: {self.test_exe.name if self.test_exe else '未找到'}") + print(f"总耗时: {test_time:.1f}秒") + + if test_passed: + print(f"\n下一步: 更新组件管理器以支持物理模块") + print(f"建议: 修改component_manager.f90,添加physics组件创建") + return 0 + else: + if args.force: + self.print_warning("测试失败,但由于--flag继续执行") + return 0 + return 1 + + except KeyboardInterrupt: + self.print_error("\n测试被用户中断") + return 1 + except Exception as e: + self.print_error(f"测试过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + system = Step2System() + return system.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03b/src/CMakeLists.txt new file mode 100644 index 00000000..59ad3c3f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/CMakeLists.txt @@ -0,0 +1,31 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(base) +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) +add_subdirectory(physics) # ← 新增物理模块目录 +add_subdirectory(manager) +add_subdirectory(solver) + +message(STATUS "源代码目录配置完成") + +add_executable(run_eno_weno + ${CMAKE_CURRENT_SOURCE_DIR}/run_eno_weno.f90 +) + +target_link_libraries(run_eno_weno + PRIVATE + solver + infrastructure + core + physics + manager +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/base/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03b/src/base/CMakeLists.txt new file mode 100644 index 00000000..74f4aa65 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/base/CMakeLists.txt @@ -0,0 +1,16 @@ +# src/base/CMakeLists.txt +message(STATUS "Configuring base module...") + +add_library(base STATIC + modules.f90 + precision.f90 # 新增 +) + +set_target_properties(base PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Base module configured") + +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/base/modules.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/src/base/modules.f90 new file mode 100644 index 00000000..43aaee24 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/base/modules.f90 @@ -0,0 +1,36 @@ +! src/base/modules.f90 +module base_modules + use, intrinsic :: iso_fortran_env, only: real64, int32 + implicit none + + public :: wp, ip, max_name_len, string_len, cfd_config_base, component_info + + integer, parameter :: wp = real64 + integer, parameter :: ip = int32 + integer, parameter :: string_len = 100 + integer, parameter :: max_name_len = 32 + + ! 基础配置类型 + type :: cfd_config_base + character(len=max_name_len) :: ic_type = "step" + character(len=max_name_len) :: recon_scheme = "eno" + character(len=max_name_len) :: flux_type = "rusanov" + integer(ip) :: rk_order = 1 + real(wp) :: wave_speed = 1.0_wp + real(wp) :: final_time = 0.625_wp + real(wp) :: dt = 0.025_wp + character(len=max_name_len) :: boundary_type = "periodic" + integer(ip) :: spatial_order = 2 + character(len=max_name_len) :: equation_type = "linear_advection" + character(len=max_name_len) :: problem_type = "linear_advection" + logical :: verbose = .true. + end type cfd_config_base + + ! 组件信息类型 + type :: component_info + character(len=max_name_len) :: category = "" + character(len=max_name_len) :: name = "" + integer(ip) :: order = 0 + end type component_info + +end module base_modules \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/base/precision.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/src/base/precision.f90 new file mode 100644 index 00000000..4ac5fd7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/base/precision.f90 @@ -0,0 +1,9 @@ +! src/base/precision.f90(简单版本) +module precision_module + use base_modules, only: wp, ip + implicit none + + ! 重新导出,确保兼容 + public :: wp, ip + +end module precision_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03b/src/core/CMakeLists.txt new file mode 100644 index 00000000..d8b8df06 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/core/CMakeLists.txt @@ -0,0 +1,14 @@ +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") + +add_library(core STATIC + registry.f90 +) + +target_link_libraries(core PRIVATE base) + +set_target_properties(core PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Core module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/core/factory_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/src/core/factory_base.f90 new file mode 100644 index 00000000..302418a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/core/factory_base.f90 @@ -0,0 +1,57 @@ +! src/core/factory_base.f90 +module factory_base_module + use base_modules, only: wp, ip + use registry_module, only: create_component, has_component + + implicit none + private + public :: wp, ip, factory_base, factory_create + + ! 工厂基类 + type :: factory_base + character(len=max_name_length) :: category = "" + contains + procedure :: create => factory_base_create + procedure :: get_available => factory_base_get_available + end type factory_base + + ! 便捷函数类型 + abstract interface + function factory_function_interface(category, name) result(instance) + import :: wp + character(len=*), intent(in) :: category, name + class(*), allocatable :: instance + end function factory_function_interface + end interface + +contains + + ! 创建工厂实例 + function factory_create(category) result(factory) + character(len=*), intent(in) :: category + type(factory_base) :: factory + factory%category = trim(category) + end function factory_create + + ! 工厂创建方法 + function factory_base_create(this, name) result(instance) + class(factory_base), intent(in) :: this + character(len=*), intent(in) :: name + class(*), allocatable :: instance + + instance = create_component(this%category, name) + end function factory_base_create + + ! 获取可用组件列表(简化版) + subroutine factory_base_get_available(this, names, count) + class(factory_base), intent(in) :: this + character(len=*), allocatable, intent(out) :: names(:) + integer(ip), intent(out) :: count + + ! 这里需要实现从注册表获取列表的逻辑 + ! 暂时返回空列表 + count = 0 + allocate(character(len=max_name_length) :: names(0)) + end subroutine factory_base_get_available + +end module factory_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..a960167c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/src/core/registry.f90 new file mode 100644 index 00000000..d155aa19 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/core/registry.f90 @@ -0,0 +1,257 @@ +! src/core/registry.f90 (更新版) +module registry_module + use base_modules, only: wp, ip, max_name_len, component_info + + implicit none + private + + ! 明确公开所有需要的接口 + public :: wp, ip ! 类型参数 + public :: component_info ! 类型 + public :: registry_init, registry_cleanup ! 初始化/清理 + public :: register_component_simple ! 注册组件 + public :: has_component_simple ! 检查组件 + public :: list_components ! 列出组件 + public :: registry_is_initialized ! 检查初始化状态 + public :: registry_get_size ! 获取大小 + public :: initialize_default_components ! 新增:初始化默认组件 + + ! 全局注册表 + type :: component_registry + type(component_info), allocatable :: components(:) + integer(ip) :: count = 0 + integer(ip) :: capacity = 100 + logical :: initialized = .false. + logical :: verbose = .true. + logical :: default_components_added = .false. ! 新增:标记是否已添加默认组件 + end type component_registry + + type(component_registry) :: registry + +contains + + ! ==================== 公共API ==================== + + subroutine registry_init(verbose) + logical, optional, intent(in) :: verbose + + if (registry%initialized) then + if (registry%verbose) then + print *, "[REGISTRY] Already initialized" + end if + return + end if + + if (present(verbose)) then + registry%verbose = verbose + end if + + allocate(registry%components(registry%capacity)) + registry%initialized = .true. + + if (registry%verbose) then + print *, "[REGISTRY] Initialized with capacity:", registry%capacity + end if + end subroutine registry_init + + subroutine registry_cleanup() + if (allocated(registry%components)) then + deallocate(registry%components) + end if + registry%initialized = .false. + registry%count = 0 + registry%default_components_added = .false. ! 重置标记 + + if (registry%verbose) then + print *, "[REGISTRY] Cleaned up" + end if + end subroutine registry_cleanup + + ! 新增:初始化默认组件 + subroutine initialize_default_components() + if (.not. registry%initialized) then + call registry_init() + end if + + if (registry%default_components_added) then + if (registry%verbose) then + print *, "[REGISTRY] Default components already added" + end if + return + end if + + ! 注册重构器 + call register_component_simple("reconstructor", "eno", order=3) + call register_component_simple("reconstructor", "weno3", order=3) + call register_component_simple("reconstructor", "weno5", order=5) + + ! 注册通量计算器 + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + + ! 注册边界条件 + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + + ! 注册时间积分器 + call register_component_simple("integrator", "rk1", order=1) + call register_component_simple("integrator", "rk2", order=2) + call register_component_simple("integrator", "rk3", order=3) + + ! 注册方程 + call register_component_simple("equation", "linear_advection") + + ! 注册问题 + call register_component_simple("problem", "linear_advection") + + ! 注册初始条件 + call register_component_simple("initial_condition", "step") + call register_component_simple("initial_condition", "sin") + call register_component_simple("initial_condition", "gaussian") + + registry%default_components_added = .true. + + if (registry%verbose) then + print *, "[REGISTRY] Default components registered" + print *, "[REGISTRY] Total components:", registry%count + end if + end subroutine initialize_default_components + + subroutine register_component_simple(category, name, order) + character(len=*), intent(in) :: category, name + integer(ip), optional, intent(in) :: order + + integer(ip) :: i + type(component_info) :: info + + if (.not. registry%initialized) then + call registry_init() + end if + + ! 检查是否已存在 + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + if (registry%verbose) then + print *, "[WARN] Overwriting component: ", trim(category), ".", trim(name) + end if + + ! 更新 + if (present(order)) then + registry%components(i)%order = order + else + registry%components(i)%order = 0 + end if + return + end if + end do + + ! 扩展数组 + if (registry%count >= registry%capacity) then + call expand_registry() + end if + + ! 添加新组件 + registry%count = registry%count + 1 + + info%category = trim(category) + info%name = trim(name) + info%order = 0 + if (present(order)) then + info%order = order + end if + + registry%components(registry%count) = info + + if (registry%verbose) then + print *, "[OK] Registered simple: ", trim(category), ".", trim(name) + end if + end subroutine register_component_simple + + logical function has_component_simple(category, name) + character(len=*), intent(in) :: category, name + + integer(ip) :: i + + has_component_simple = .false. + + if (.not. registry%initialized) return + + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + has_component_simple = .true. + return + end if + end do + end function has_component_simple + + subroutine list_components(category) + character(len=*), optional, intent(in) :: category + + integer(ip) :: i, count + + if (.not. registry%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + if (registry%count == 0) then + print *, "[INFO] No components registered" + return + end if + + count = 0 + print *, "=== Registry Contents ===" + do i = 1, registry%count + if (.not. present(category) .or. & + trim(registry%components(i)%category) == trim(category)) then + call print_component_info(registry%components(i)) + count = count + 1 + end if + end do + + print *, "Total:", count, "components" + print *, "==========================" + end subroutine list_components + + ! ==================== 新增函数 ==================== + + logical function registry_is_initialized() + ! 检查注册表是否已初始化 + registry_is_initialized = registry%initialized + end function registry_is_initialized + + integer(ip) function registry_get_size() + ! 获取注册表中的组件数量 + registry_get_size = registry%count + end function registry_get_size + + ! ==================== 内部辅助函数 ==================== + + subroutine expand_registry() + type(component_info), allocatable :: temp(:) + + registry%capacity = registry%capacity * 2 + allocate(temp(registry%capacity)) + temp(1:registry%count) = registry%components(1:registry%count) + call move_alloc(temp, registry%components) + + if (registry%verbose) then + print *, "[INFO] Registry expanded to capacity:", registry%capacity + end if + end subroutine expand_registry + + subroutine print_component_info(info) + type(component_info), intent(in) :: info + + if (info%order > 0) then + print *, " [", trim(info%category), ".", trim(info%name), & + " (order:", info%order, ")]" + else + print *, " [", trim(info%category), ".", trim(info%name), "]" + end if + end subroutine print_component_info + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/core/registry_initializer.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/src/core/registry_initializer.f90 new file mode 100644 index 00000000..44023d1d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/core/registry_initializer.f90 @@ -0,0 +1,39 @@ +! src/core/registry_initializer.f90 (新增文件) +module registry_initializer_module + use registry_module, only: register_component_simple + implicit none + private + public :: initialize_default_registry + +contains + + subroutine initialize_default_registry() + ! 注册重构器 + call register_component_simple("reconstructor", "eno", order=3) + call register_component_simple("reconstructor", "weno3", order=3) + call register_component_simple("reconstructor", "weno5", order=5) + + ! 注册通量计算器 + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + + ! 注册边界条件 + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + + ! 注册时间积分器 + call register_component_simple("integrator", "rk1", order=1) + call register_component_simple("integrator", "rk2", order=2) + call register_component_simple("integrator", "rk3", order=3) + + ! 注册方程 + call register_component_simple("equation", "linear_advection") + + ! 注册问题 + call register_component_simple("problem", "linear_advection") + + print *, "[REGISTRY] Default components registered" + end subroutine initialize_default_registry + +end module registry_initializer_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03b/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..70cbbd2f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/infrastructure/CMakeLists.txt @@ -0,0 +1,17 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "Configuring infrastructure module...") + +add_library(infrastructure STATIC + config.f90 + mesh.f90 + domain.f90 # 新增 + solution.f90 # 新增 +) + +target_link_libraries(infrastructure PRIVATE base) + +set_target_properties(infrastructure PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Infrastructure module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/src/infrastructure/config.f90 new file mode 100644 index 00000000..7586a1a5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/infrastructure/config.f90 @@ -0,0 +1,144 @@ +! src/infrastructure/config.f90 (修复版) +module config_module + use base_modules, only: wp, ip, max_name_len, cfd_config_base + + implicit none + public :: wp, ip, cfd_config, config_print, config_with_reconstruction + + ! 扩展配置类型 - 添加物理相关字段 + type, extends(cfd_config_base) :: cfd_config + ! 物理参数 + real(wp) :: left_boundary_value = 1.0_wp + real(wp) :: right_boundary_value = 2.0_wp + real(wp) :: domain_length = 2.0_wp + + ! 新增:物理模块相关配置 + real(wp) :: pulse_center = 0.5_wp ! 高斯脉冲中心 + real(wp) :: pulse_width = 0.1_wp ! 高斯脉冲宽度 + logical :: enable_physics = .true. ! 是否启用物理模块 + contains + ! 新增:物理相关配置方法 + procedure :: set_physics_parameters + procedure :: get_physics_info + end type cfd_config + +contains + + subroutine config_print(cfg) + type(cfd_config), intent(in) :: cfg + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(cfg%ic_type) + print *, "Reconstruction: ", trim(cfg%recon_scheme), " (order:", cfg%spatial_order, ")" + print *, "Flux type: ", trim(cfg%flux_type) + print *, "Time integration: RK", cfg%rk_order + print *, "Wave speed: ", cfg%wave_speed + print *, "Final time: ", cfg%final_time + print *, "Time step: ", cfg%dt + print *, "Boundary: ", trim(cfg%boundary_type) + + ! 新增:物理配置信息 + print *, "--- Physics Configuration ---" + print *, "Equation type: ", trim(cfg%equation_type) + print *, "Problem type: ", trim(cfg%problem_type) + print *, "Domain length: ", cfg%domain_length + print *, "Physics enabled: ", cfg%enable_physics + + if (cfg%ic_type == "gaussian") then + print *, "Pulse center: ", cfg%pulse_center + print *, "Pulse width: ", cfg%pulse_width + end if + + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(cfg, scheme, order) + type(cfd_config), intent(inout) :: cfg + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + integer :: i + + ! 转换为小写 + cfg%recon_scheme = scheme + do i = 1, len_trim(cfg%recon_scheme) + if (cfg%recon_scheme(i:i) >= 'A' .and. cfg%recon_scheme(i:i) <= 'Z') then + cfg%recon_scheme(i:i) = char(ichar(cfg%recon_scheme(i:i)) + 32) + end if + end do + + ! 设置阶数 + if (present(order)) then + cfg%spatial_order = order + else + if (index(cfg%recon_scheme, 'weno') > 0) then + cfg%spatial_order = 5 + else if (trim(cfg%recon_scheme) == 'eno') then + cfg%spatial_order = 3 + end if + end if + + if (cfg%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(cfg%recon_scheme), & + " Order: ", cfg%spatial_order + end if + end subroutine config_with_reconstruction + + ! ========== 新增:物理参数设置方法 ========== + + subroutine set_physics_parameters(this, equation_type, problem_type, & + domain_length, enable_physics) + class(cfd_config), intent(inout) :: this + character(len=*), intent(in), optional :: equation_type, problem_type + real(wp), intent(in), optional :: domain_length + logical, intent(in), optional :: enable_physics + + if (present(equation_type)) then + this%equation_type = trim(equation_type) + if (this%verbose) then + print *, "[CONFIG] Set equation type: ", trim(this%equation_type) + end if + end if + + if (present(problem_type)) then + this%problem_type = trim(problem_type) + if (this%verbose) then + print *, "[CONFIG] Set problem type: ", trim(this%problem_type) + end if + end if + + if (present(domain_length)) then + this%domain_length = domain_length + if (this%verbose) then + print *, "[CONFIG] Set domain length: ", this%domain_length + end if + end if + + if (present(enable_physics)) then + this%enable_physics = enable_physics + if (this%verbose) then + print *, "[CONFIG] Physics module enabled: ", this%enable_physics + end if + end if + end subroutine set_physics_parameters + + subroutine get_physics_info(this) + class(cfd_config), intent(in) :: this + + print *, "=== Physics Configuration Info ===" + print *, "Equation type: ", trim(this%equation_type) + print *, "Problem type: ", trim(this%problem_type) + print *, "Domain length: ", this%domain_length + print *, "Wave speed: ", this%wave_speed + print *, "Physics enabled: ", this%enable_physics + + if (this%ic_type == "gaussian") then + print *, "Pulse parameters:" + print *, " Center: ", this%pulse_center + print *, " Width: ", this%pulse_width + end if + + print *, "==================================" + end subroutine get_physics_info + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/infrastructure/domain.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/src/infrastructure/domain.f90 new file mode 100644 index 00000000..c3662f03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/infrastructure/domain.f90 @@ -0,0 +1,102 @@ +! src/infrastructure/domain.f90 +module domain_module + use base_modules, only: wp, ip, max_name_len + use config_module, only: cfd_config + use mesh_module, only: mesh_type + + implicit none + private + public :: wp, ip, domain_type, domain_create, is_physical_cell + + type :: domain_type + type(cfd_config), pointer :: config => null() + type(mesh_type), pointer :: mesh => null() + integer(ip) :: nghosts = 0 + integer(ip) :: ist = 1 ! 物理区域起始索引(1-based) + integer(ip) :: ied = 1 ! 物理区域结束索引(exclusive) + integer(ip) :: ntcells = 0 ! 总单元数(含ghost) + contains + procedure :: print_info => domain_print_info + procedure :: get_physical_indices => domain_get_physical_indices + end type domain_type + +contains + + function domain_create(config, mesh) result(domain) + type(cfd_config), target, intent(in) :: config + type(mesh_type), target, intent(in) :: mesh + type(domain_type) :: domain + + domain%config => config + domain%mesh => mesh + + ! 计算ghost层数(参考Julia的_calc_nghosts) + domain%nghosts = calc_nghosts(config) + domain%ist = domain%nghosts + 1 + domain%ied = domain%ist + mesh%ncells + domain%ntcells = mesh%ncells + 2 * domain%nghosts + + if (config%verbose) then + print *, "[DOMAIN] Created:" + print *, " Ghost layers: ", domain%nghosts + print *, " Physical cells: ", domain%ist, " to ", domain%ied - 1 + print *, " Total cells: ", domain%ntcells + end if + end function domain_create + + function calc_nghosts(config) result(nghosts) + type(cfd_config), intent(in) :: config + integer(ip) :: nghosts + + character(len=max_name_len) :: scheme + + scheme = config%recon_scheme + + if (scheme == "eno") then + nghosts = config%spatial_order + else if (index(scheme, "weno") > 0) then + nghosts = config%spatial_order / 2 + 1 + else + print *, "[WARNING] Unknown scheme, using default nghosts=2" + nghosts = 2 + end if + + if (nghosts <= 0) then + print *, "[ERROR] Invalid nghosts: ", nghosts + nghosts = 2 + end if + end function calc_nghosts + + logical function is_physical_cell(this, idx) + class(domain_type), intent(in) :: this + integer(ip), intent(in) :: idx + is_physical_cell = (idx >= this%ist .and. idx < this%ied) + end function is_physical_cell + + function domain_get_physical_indices(this) result(indices) + class(domain_type), intent(in) :: this + integer(ip), allocatable :: indices(:) + integer(ip) :: i, count + + count = this%ied - this%ist + allocate(indices(count)) + + do i = 1, count + indices(i) = this%ist + i - 1 + end do + end function domain_get_physical_indices + + subroutine domain_print_info(this) + class(domain_type), intent(in) :: this + + print *, "=== Domain Information ===" + print *, "Configuration: ", trim(this%config%recon_scheme), & + " order ", this%config%spatial_order + print *, "Ghost layers: ", this%nghosts + print *, "Physical cells: ", this%ist, " to ", this%ied - 1 + print *, "Total cells: ", this%ntcells + print *, "Mesh cells: ", this%mesh%ncells + print *, "==========================" + end subroutine domain_print_info + +end module domain_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..f810f3a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/infrastructure/mesh.f90 @@ -0,0 +1,73 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use base_modules, only: wp, ip + + implicit none + public :: wp, ip, mesh_type, mesh_init, mesh_print_info + + ! 网格类型 + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer(ip) :: ncells = 40 + integer(ip) :: nnodes + integer(ip) :: nx + real(wp), allocatable :: x(:) ! 节点坐标 + real(wp), allocatable :: xcc(:) ! 单元中心坐标 + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer(ip), optional, intent(in) :: ncells + + integer(ip) :: i + + ! 设置参数 + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! 计算 + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! 分配内存 + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! 生成节点坐标 + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! 生成单元中心坐标 + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== Mesh Information ===" + print *, "Domain: [", this%xmin, ", ", this%xmax, "]" + print *, "Cells: ", this%ncells + print *, "Nodes: ", this%nnodes + print *, "dx: ", this%dx + print *, "L: ", this%L + print *, "========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/infrastructure/solution.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/src/infrastructure/solution.f90 new file mode 100644 index 00000000..ce88fd8a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/infrastructure/solution.f90 @@ -0,0 +1,131 @@ +! src/infrastructure/solution.f90 +module solution_module + use base_modules, only: wp, ip + use domain_module, only: domain_type + + implicit none + private + public :: wp, ip, solution_type, solution_create, solution_reset + + type :: solution_type + type(domain_type), pointer :: domain => null() + real(wp), allocatable :: u(:) ! 当前解(含ghost) + real(wp), allocatable :: un(:) ! 旧解 + real(wp), allocatable :: q_face_left(:) ! 左界面值 + real(wp), allocatable :: q_face_right(:)! 右界面值 + real(wp), allocatable :: flux(:) ! 通量 + real(wp), allocatable :: res(:) ! 残差 + contains + procedure :: initialize => solution_initialize + procedure :: update_old_field => solution_update_old_field + procedure :: print_info => solution_print_info + procedure :: reset => solution_reset_instance + end type solution_type + +contains + + function solution_create(domain) result(solution) + type(domain_type), target, intent(in) :: domain + type(solution_type) :: solution + + integer(ip) :: ncells, nnodes, ntcells + + solution%domain => domain + + ncells = domain%mesh%ncells + nnodes = domain%mesh%nnodes + ntcells = domain%ntcells + + ! 分配数组(与Julia solution.jl一致) + allocate(solution%u(ntcells), source=0.0_wp) + allocate(solution%un(ntcells), source=0.0_wp) + allocate(solution%q_face_left(nnodes), source=0.0_wp) + allocate(solution%q_face_right(nnodes), source=0.0_wp) + allocate(solution%flux(nnodes), source=0.0_wp) + allocate(solution%res(ncells), source=0.0_wp) + + if (domain%config%verbose) then + print *, "[SOLUTION] Created:" + print *, " u size: ", size(solution%u), " (with ghosts)" + print *, " flux size: ", size(solution%flux) + print *, " res size: ", size(solution%res) + end if + end function solution_create + + subroutine solution_initialize(this, initial_values) + class(solution_type), intent(inout) :: this + real(wp), intent(in), optional :: initial_values(:) + + integer(ip) :: i, idx + type(domain_type), pointer :: domain + + domain => this%domain + + if (present(initial_values)) then + ! 应用初始值到物理区域 + do i = domain%ist, domain%ied - 1 + idx = i - domain%ist + 1 + if (idx <= size(initial_values)) then + this%u(i) = initial_values(idx) + end if + end do + else + ! 默认为0 + this%u = 0.0_wp + end if + + ! 同步旧场(与Julia的update_old_field一致) + call this%update_old_field() + + if (domain%config%verbose) then + print *, "[SOLUTION] Initialized" + print *, " u range: ", minval(this%u), " to ", maxval(this%u) + end if + end subroutine solution_initialize + + subroutine solution_update_old_field(this) + class(solution_type), intent(inout) :: this + this%un = this%u ! 与Julia的 un .= u 一致 + end subroutine solution_update_old_field + + subroutine solution_reset_instance(this) + class(solution_type), intent(inout) :: this + call solution_reset(this) + end subroutine solution_reset_instance + + subroutine solution_reset(solution) + type(solution_type), intent(inout) :: solution + + if (allocated(solution%u)) solution%u = 0.0_wp + if (allocated(solution%un)) solution%un = 0.0_wp + if (allocated(solution%q_face_left)) solution%q_face_left = 0.0_wp + if (allocated(solution%q_face_right)) solution%q_face_right = 0.0_wp + if (allocated(solution%flux)) solution%flux = 0.0_wp + if (allocated(solution%res)) solution%res = 0.0_wp + + if (associated(solution%domain) .and. solution%domain%config%verbose) then + print *, "[SOLUTION] Reset" + end if + end subroutine solution_reset + + subroutine solution_print_info(this) + class(solution_type), intent(in) :: this + + print *, "=== Solution Information ===" + print *, "Arrays:" + print *, " u: ", size(this%u), " elements" + print *, " un: ", size(this%un), " elements" + print *, " q_face_left: ", size(this%q_face_left), " elements" + print *, " q_face_right: ", size(this%q_face_right), " elements" + print *, " flux: ", size(this%flux), " elements" + print *, " res: ", size(this%res), " elements" + + if (allocated(this%u)) then + print *, "Values:" + print *, " u min/max: ", minval(this%u), maxval(this%u) + print *, " un min/max: ", minval(this%un), maxval(this%un) + end if + print *, "============================" + end subroutine solution_print_info + +end module solution_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/manager/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03b/src/manager/CMakeLists.txt new file mode 100644 index 00000000..00c8bf49 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/manager/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/manager/CMakeLists.txt +message(STATUS "配置管理器模块...") + +# 创建管理器库 +add_library(manager STATIC + component_manager.f90 + component_factory.f90 +) + +# 明确依赖关系:管理器依赖所有其他模块 +target_link_libraries(manager + PRIVATE + core + infrastructure + reconstructor + flux +) + +# 设置模块输出目录 +set_target_properties(manager PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "管理器模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/manager/component_factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/src/manager/component_factory.f90 new file mode 100644 index 00000000..de8cbf1a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/manager/component_factory.f90 @@ -0,0 +1,142 @@ +! src/manager/component_factory.f90 (完整文件) +module component_factory_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + use eno_reconstructor_module, only: eno_reconstructor, create_eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor, create_weno3_reconstructor + use weno5_reconstructor_module, only: weno5_reconstructor, create_weno5_reconstructor + use rusanov_flux_module, only: rusanov_flux, create_rusanov_flux + + implicit none + private + public :: wp, create_reconstructor, create_flux_calculator + + ! 错误代码 + integer, parameter :: CM_SUCCESS = 0 + integer, parameter :: CM_ERROR_UNKNOWN_SCHEME = 1 + integer, parameter :: CM_ERROR_UNKNOWN_FLUX = 2 + integer, parameter :: CM_ERROR_INVALID_ORDER = 3 + +contains + + ! ==================== 重构器创建 ==================== + + function create_reconstructor(config, status) result(recon) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(reconstructor_base), allocatable :: recon + + character(len=20) :: scheme + integer :: order, error_code + + scheme = trim(adjustl(config%recon_scheme)) + order = config%spatial_order + + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating reconstructor: ", scheme, " order=", order + end if + + ! 处理"weno"作为WENO5的别名(与Julia一致) + if (scheme == "weno" .and. order == 5) then + scheme = "weno5" + end if + + select case(scheme) + case('eno') + allocate(eno_reconstructor :: recon) + select type(recon) + type is(eno_reconstructor) + recon = create_eno_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case('weno3') + allocate(weno3_reconstructor :: recon) + select type(recon) + type is(weno3_reconstructor) + recon = create_weno3_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case('weno5') + allocate(weno5_reconstructor :: recon) + select type(recon) + type is(weno5_reconstructor) + recon = create_weno5_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case default + error_code = CM_ERROR_UNKNOWN_SCHEME + if (config%verbose) then + print *, "[ERROR] Unknown reconstructor scheme: ", scheme + print *, " Available: eno, weno3, weno5" + end if + end select + + ! 检查阶数有效性 + if (error_code == CM_SUCCESS) then + if (order < 1) then + error_code = CM_ERROR_INVALID_ORDER + if (config%verbose) then + print *, "[ERROR] Invalid spatial order: ", order + end if + end if + end if + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Reconstructor creation failed" + end if + end function create_reconstructor + + ! ==================== 通量计算器创建 ==================== + + function create_flux_calculator(config, status) result(flux) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(flux_calculator_base), allocatable :: flux + + character(len=20) :: flux_type + integer :: error_code + + flux_type = trim(adjustl(config%flux_type)) + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating flux calculator: ", flux_type + end if + + select case(flux_type) + case('rusanov') + allocate(rusanov_flux :: flux) + select type(flux) + type is(rusanov_flux) + flux = create_rusanov_flux() + flux%wave_speed_default = config%wave_speed + end select + + case default + error_code = CM_ERROR_UNKNOWN_FLUX + if (config%verbose) then + print *, "[ERROR] Unknown flux type: ", flux_type + print *, " Available: rusanov" + end if + end select + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Flux calculator creation failed" + end if + end function create_flux_calculator + +end module component_factory_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/manager/component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/src/manager/component_manager.f90 new file mode 100644 index 00000000..25eac29b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/manager/component_manager.f90 @@ -0,0 +1,76 @@ +! src/manager/component_manager.f90 (完整文件) +module component_manager_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use component_factory_module, only: create_reconstructor, create_flux_calculator + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + implicit none + private + public :: wp, component_manager_info, validate_config + public :: create_reconstructor, create_flux_calculator + +contains + + ! ==================== 配置验证 ==================== + + function validate_config(config) result(is_valid) + type(cfd_config), intent(in) :: config + logical :: is_valid + + integer :: status + class(reconstructor_base), allocatable :: test_recon + class(flux_calculator_base), allocatable :: test_flux + + is_valid = .false. + + ! 测试创建重构器 + test_recon = create_reconstructor(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid reconstructor configuration" + end if + return + end if + + ! 测试创建通量计算器 + test_flux = create_flux_calculator(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid flux configuration" + end if + return + end if + + ! 清理测试组件 + if (allocated(test_recon)) deallocate(test_recon) + if (allocated(test_flux)) deallocate(test_flux) + + is_valid = .true. + + if (config%verbose) then + print *, "[CONFIG VALIDATION] Configuration is valid" + end if + end function validate_config + + ! ==================== 信息显示 ==================== + + subroutine component_manager_info() + print *, "=== Component Manager ===" + print *, "Available reconstructors:" + print *, " - eno (orders: 1-7)" + print *, " - weno3 (order: 3)" + print *, " - weno5 (order: 5)" + print *, "" + print *, "Available flux calculators:" + print *, " - rusanov" + print *, "" + print *, "Features:" + print *, " - Configuration validation" + print *, " - Component creation from config" + print *, " - Error handling with status codes" + print *, "=========================" + end subroutine component_manager_info + +end module component_manager_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..3fde7746 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,28 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux STATIC + base.f90 + rusanov.f90 +) + +#target_link_libraries(flux PRIVATE core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 明确设置不使用 /Qm64 选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + set_target_properties(flux PROPERTIES + Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /Qm64" # 明确设置,避免继承问题 + ) +endif() + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/flux/base.f90 new file mode 100644 index 00000000..7080a7ae --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/flux/base.f90 @@ -0,0 +1,30 @@ +!src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + procedure :: print_basic_info => flux_print_basic ! 添加辅助方法 + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + + subroutine flux_print_basic(this) + class(flux_calculator_base), intent(in) :: this + print *, " Name: ", trim(this%name) + end subroutine flux_print_basic + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..daa9e3bb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/flux/rusanov.f90 @@ -0,0 +1,41 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux, create_rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + + ! 构造函数接口 + interface rusanov_flux + module procedure create_rusanov_flux + end interface + +contains + + ! 构造函数 + type(rusanov_flux) function create_rusanov_flux() result(this) + this%name = "Rusanov" + this%wave_speed_default = 1.0_real64 + end function create_rusanov_flux + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Flux calculator information:" + call this%print_basic_info() + + ! 添加Rusanov特有信息 + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..c88ea647 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,23 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor STATIC + base.f90 + eno.f90 + weno3.f90 + weno5.f90 +) + +target_link_libraries(reconstructor PRIVATE core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..53798d02 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/reconstructor/base.f90 @@ -0,0 +1,33 @@ +!src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + procedure :: print_basic_info => reconstructor_print_basic ! 添加一个辅助方法 + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + + subroutine reconstructor_print_basic(this) + class(reconstructor_base), intent(in) :: this + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_print_basic + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..f973e8b3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor, create_eno_reconstructor ! ← 添加这个 + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + + ! 构造函数接口 + interface eno_reconstructor + module procedure create_eno_reconstructor + end interface + +contains + + ! 构造函数 + type(eno_reconstructor) function create_eno_reconstructor() result(this) + this%name = "ENO" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_eno_reconstructor + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加ENO特有信息 + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..d5b7a747 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno3_reconstructor, create_weno3_reconstructor + + type, extends(reconstructor_base) :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + + ! 构造函数接口 + interface weno3_reconstructor + module procedure create_weno3_reconstructor + end interface + +contains + + ! 构造函数 + type(weno3_reconstructor) function create_weno3_reconstructor() result(this) + this%name = "WENO3" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_weno3_reconstructor + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO3特有信息 + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/reconstructor/weno5.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/reconstructor/weno5.f90 new file mode 100644 index 00000000..a869c67d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/numerics/reconstructor/weno5.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/weno5.f90(新增) +module weno5_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno5_reconstructor, create_weno5_reconstructor + + type, extends(reconstructor_base) :: weno5_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno5_info + end type weno5_reconstructor + + ! 构造函数接口 + interface weno5_reconstructor + module procedure create_weno5_reconstructor + end interface + +contains + + ! 构造函数 + type(weno5_reconstructor) function create_weno5_reconstructor() result(this) + this%name = "WENO5" + this%order = 5 + this%epsilon = 1.0e-6_real64 + end function create_weno5_reconstructor + + subroutine weno5_info(this) + class(weno5_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO5特有信息 + print *, " Type: WENO-5 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno5_info + +end module weno5_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/physics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03b/src/physics/CMakeLists.txt new file mode 100644 index 00000000..cc4e233a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/physics/CMakeLists.txt @@ -0,0 +1,19 @@ +# src/physics/CMakeLists.txt +message(STATUS "配置物理模块...") + +# 创建物理模块库 +add_library(physics STATIC + physics_interface.f90 + equations/linear_convection.f90 + problems/linear_convection_problem.f90 +) + +# 链接依赖 +target_link_libraries(physics PRIVATE base) + +# 设置模块输出目录 +set_target_properties(physics PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "物理模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/physics/equations/linear_convection.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/src/physics/equations/linear_convection.f90 new file mode 100644 index 00000000..fff7be55 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/physics/equations/linear_convection.f90 @@ -0,0 +1,49 @@ +! src/physics/equations/linear_convection.f90 +module linear_convection_equation + use precision_module, only: wp, ip + use physics_interface, only: physics_equation + implicit none + private + + ! 具体方程类型 - 先声明 + type, extends(physics_equation) :: linear_convection_eq + real(wp) :: wave_speed = 1.0_wp + contains + procedure :: flux => lc_flux + procedure :: speed => lc_speed + end type linear_convection_eq + + ! 公开接口 + public :: wp, ip + public :: linear_convection_eq, create_linear_convection_eq + +contains + + ! 构造函数 + function create_linear_convection_eq(wave_speed) result(eq) + real(wp), intent(in), optional :: wave_speed + type(linear_convection_eq) :: eq + + eq%name = "Linear Convection" + if (present(wave_speed)) then + eq%wave_speed = wave_speed + else + eq%wave_speed = 1.0_wp + end if + end function create_linear_convection_eq + + ! 方法实现 + pure function lc_flux(this, u) result(f) + class(linear_convection_eq), intent(in) :: this + real(wp), intent(in) :: u + real(wp) :: f + f = this%wave_speed * u + end function lc_flux + + pure function lc_speed(this) result(a) + class(linear_convection_eq), intent(in) :: this + real(wp) :: a + a = this%wave_speed + end function lc_speed + +end module linear_convection_equation \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/physics/physics_interface.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/src/physics/physics_interface.f90 new file mode 100644 index 00000000..45002da6 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/physics/physics_interface.f90 @@ -0,0 +1,64 @@ +! src/physics/physics_interface.f90 +module physics_interface + use precision_module, only: wp, ip + implicit none + private + + ! 定义抽象基类型 - 先声明为私有,然后在公开部分导出 + type, abstract :: physics_equation + character(len=:), allocatable :: name + contains + procedure(eq_flux_abs), deferred :: flux + procedure(eq_speed_abs), deferred :: speed + end type physics_equation + + type, abstract :: physics_problem + character(len=:), allocatable :: name + contains + procedure(prob_ic_abs), deferred :: initial_condition + procedure(prob_bc_abs), deferred :: boundary_condition + procedure(prob_exact_abs), deferred :: exact_solution + end type physics_problem + + ! 抽象接口定义 + abstract interface + pure function eq_flux_abs(this, u) result(f) + import :: physics_equation, wp + class(physics_equation), intent(in) :: this + real(wp), intent(in) :: u + real(wp) :: f + end function eq_flux_abs + + pure function eq_speed_abs(this) result(a) + import :: physics_equation, wp + class(physics_equation), intent(in) :: this + real(wp) :: a + end function eq_speed_abs + + subroutine prob_ic_abs(this, x, u) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp), intent(out) :: u(:) + end subroutine prob_ic_abs + + subroutine prob_bc_abs(this, u, t) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(inout) :: u(:) + real(wp), intent(in), optional :: t + end subroutine prob_bc_abs + + function prob_exact_abs(this, x, t) result(u) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(in) :: x(:), t + real(wp), dimension(size(x)) :: u + end function prob_exact_abs + end interface + + ! 公开接口 - 使用独立的public语句 + public :: wp, ip + public :: physics_equation, physics_problem + +end module physics_interface \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/physics/problems/linear_convection_problem.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/src/physics/problems/linear_convection_problem.f90 new file mode 100644 index 00000000..06226ed1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/physics/problems/linear_convection_problem.f90 @@ -0,0 +1,118 @@ +! src/physics/problems/linear_convection_problem.f90 +module linear_convection_problem + use precision_module, only: wp, ip + use physics_interface, only: physics_problem + implicit none + private + + ! 具体问题类型 - 先声明 + type, extends(physics_problem) :: linear_convection_prob + real(wp) :: wave_speed = 1.0_wp + real(wp) :: domain_length = 2.0_wp + character(len=20) :: ic_type = "step" + character(len=20) :: boundary_type = "periodic" + contains + procedure :: initial_condition => lc_initial_condition + procedure :: boundary_condition => lc_boundary_condition + procedure :: exact_solution => lc_exact_solution + end type linear_convection_prob + + ! 公开接口 + public :: wp, ip + public :: linear_convection_prob, create_linear_convection_prob + +contains + + ! 构造函数 + function create_linear_convection_prob(wave_speed, domain_length, & + ic_type, boundary_type) result(prob) + real(wp), intent(in), optional :: wave_speed, domain_length + character(len=*), intent(in), optional :: ic_type, boundary_type + type(linear_convection_prob) :: prob + + prob%name = "Linear Convection Problem" + + if (present(wave_speed)) prob%wave_speed = wave_speed + if (present(domain_length)) prob%domain_length = domain_length + if (present(ic_type)) prob%ic_type = ic_type + if (present(boundary_type)) prob%boundary_type = boundary_type + end function create_linear_convection_prob + + ! 初始条件 + subroutine lc_initial_condition(this, x, u) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp), intent(out) :: u(:) + + integer :: i + + select case (trim(this%ic_type)) + case ("step") + do i = 1, size(x) + if (x(i) >= 0.5_wp .and. x(i) <= 1.0_wp) then + u(i) = 2.0_wp + else + u(i) = 1.0_wp + end if + end do + + case ("sin", "sine") + do i = 1, size(x) + u(i) = sin(2.0_wp * 3.141592653589793_wp * x(i) / this%domain_length) + end do + + case ("gaussian") + do i = 1, size(x) + u(i) = exp(-((x(i) - 0.5_wp) / 0.1_wp)**2) + end do + + case default + ! 默认阶跃函数 + do i = 1, size(x) + if (x(i) >= 0.5_wp .and. x(i) <= 1.0_wp) then + u(i) = 2.0_wp + else + u(i) = 1.0_wp + end if + end do + end select + end subroutine lc_initial_condition + + ! 边界条件(虚拟实现,实际在boundary模块) + subroutine lc_boundary_condition(this, u, t) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(inout) :: u(:) + real(wp), intent(in), optional :: t + + ! 边界条件将在独立模块实现 + print *, "[PROBLEM] Boundary condition placeholder" + if (present(t)) then + print *, " Time = ", t + end if + end subroutine lc_boundary_condition + + ! 精确解(周期性平移) + function lc_exact_solution(this, x, t) result(u) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(in) :: x(:), t + real(wp), dimension(size(x)) :: u + real(wp), dimension(size(x)) :: x_shifted + integer :: i + + ! 周期性平移 + do i = 1, size(x) + x_shifted(i) = x(i) - this%wave_speed * t + ! 确保在 [0, domain_length) 范围内 + do while (x_shifted(i) < 0.0_wp) + x_shifted(i) = x_shifted(i) + this%domain_length + end do + do while (x_shifted(i) >= this%domain_length) + x_shifted(i) = x_shifted(i) - this%domain_length + end do + end do + + ! 重用初始条件函数 + call this%initial_condition(x_shifted, u) + end function lc_exact_solution + +end module linear_convection_problem \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/run_eno_weno.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/src/run_eno_weno.f90 new file mode 100644 index 00000000..5821c5a0 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/run_eno_weno.f90 @@ -0,0 +1,171 @@ +! src/run_eno_weno.f90 +program run_eno_weno + ! 使用所有需要的模块 + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction, config_print + use mesh_module, only: mesh_type + use solver_base_module, only: SOLVER_INITIALIZED, SOLVER_COMPLETED + + use physics_solver_module, only: physics_solver + use registry_module, only: registry_init, registry_cleanup + + implicit none + + type(cfd_config) :: config_eno3, config_weno3, config_weno5 + type(mesh_type) :: mesh + type(physics_solver) :: solver_eno3, solver_weno3, solver_weno5 + + character(len=100) :: output_file + integer :: status + + print *, "=== ENO/WENO对比分析 (Fortran版本) ===" + print *, "" + + ! 初始化注册系统 + call registry_init(verbose=.true.) + print *, "" + + ! 步骤1: 初始化网格 + print *, "步骤1: 初始化网格..." + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=40) + call mesh%print_info() + print *, "" + + ! 步骤2: 配置并运行ENO3求解器 + print *, "步骤2: 运行ENO3求解器..." + print *, "-------------------------" + + ! 创建ENO3配置 + config_eno3%verbose = .true. + call config_with_reconstruction(config_eno3, "eno", 3) + config_eno3%dt = 0.0025_wp + config_eno3%rk_order = 2 + config_eno3%wave_speed = 1.0_wp + config_eno3%final_time = 0.625_wp + config_eno3%ic_type = "step" + + print *, "ENO3配置:" + call config_print(config_eno3) + + ! 创建ENO3求解器 + print *, "创建ENO3求解器..." + solver_eno3 = physics_solver(config_eno3, mesh) + call solver_eno3%print_info() + print *, "" + + ! 初始化并运行ENO3求解器 + print *, "运行ENO3求解器..." + call solver_eno3%initialize() + call solver_eno3%run_to_time(config_eno3%final_time) + + if (solver_eno3%get_state() == SOLVER_COMPLETED) then + print *, "✓ ENO3求解器运行完成" + print *, " 最终时间: ", solver_eno3%current_time + print *, " 总步数: ", solver_eno3%current_step + else + print *, "✗ ENO3求解器运行失败" + print *, " 错误信息: ", trim(solver_eno3%get_error()) + end if + print *, "" + + ! 步骤3: 配置并运行WENO3求解器 + print *, "步骤3: 运行WENO3求解器..." + print *, "--------------------------" + + ! 创建WENO3配置 + config_weno3%verbose = .true. + call config_with_reconstruction(config_weno3, "weno3", 3) + config_weno3%dt = 0.0025_wp + config_weno3%rk_order = 2 + config_weno3%wave_speed = 1.0_wp + config_weno3%final_time = 0.625_wp + config_weno3%ic_type = "step" + + print *, "WENO3配置:" + call config_print(config_weno3) + + ! 创建WENO3求解器 + print *, "创建WENO3求解器..." + solver_weno3 = physics_solver(config_weno3, mesh) + call solver_weno3%print_info() + print *, "" + + ! 初始化并运行WENO3求解器 + print *, "运行WENO3求解器..." + call solver_weno3%initialize() + call solver_weno3%run_to_time(config_weno3%final_time) + + if (solver_weno3%get_state() == SOLVER_COMPLETED) then + print *, "✓ WENO3求解器运行完成" + print *, " 最终时间: ", solver_weno3%current_time + print *, " 总步数: ", solver_weno3%current_step + else + print *, "✗ WENO3求解器运行失败" + print *, " 错误信息: ", trim(solver_weno3%get_error()) + end if + print *, "" + + ! 步骤4: 配置并运行WENO5求解器 + print *, "步骤4: 运行WENO5求解器..." + print *, "--------------------------" + + ! 创建WENO5配置 + config_weno5%verbose = .true. + call config_with_reconstruction(config_weno5, "weno", 5) + config_weno5%dt = 0.0025_wp + config_weno5%rk_order = 2 + config_weno5%wave_speed = 1.0_wp + config_weno5%final_time = 0.625_wp + config_weno5%ic_type = "step" + + print *, "WENO5配置:" + call config_print(config_weno5) + + ! 创建WENO5求解器 + print *, "创建WENO5求解器..." + solver_weno5 = physics_solver(config_weno5, mesh) + call solver_weno5%print_info() + print *, "" + + ! 初始化并运行WENO5求解器 + print *, "运行WENO5求解器..." + call solver_weno5%initialize() + call solver_weno5%run_to_time(config_weno5%final_time) + + if (solver_weno5%get_state() == SOLVER_COMPLETED) then + print *, "✓ WENO5求解器运行完成" + print *, " 最终时间: ", solver_weno5%current_time + print *, " 总步数: ", solver_weno5%current_step + else + print *, "✗ WENO5求解器运行失败" + print *, " 错误信息: ", trim(solver_weno5%get_error()) + end if + print *, "" + + ! 清理注册系统 + call registry_cleanup() + + ! 清理求解器 + call solver_eno3%cleanup() + call solver_weno3%cleanup() + call solver_weno5%cleanup() + + print *, "=== 分析完成 ===" + print *, "总结:" + print *, " ENO3: 时间=", solver_eno3%current_time, " 步数=", solver_eno3%current_step + print *, " WENO3: 时间=", solver_weno3%current_time, " 步数=", solver_weno3%current_step + print *, " WENO5: 时间=", solver_weno5%current_time, " 步数=", solver_weno5%current_step + + if (solver_eno3%get_state() == SOLVER_COMPLETED .and. & + solver_weno3%get_state() == SOLVER_COMPLETED .and. & + solver_weno5%get_state() == SOLVER_COMPLETED) then + print *, "" + print *, "✓ 所有求解器成功运行!" + print *, "下一步: 添加边界条件模块" + else + print *, "" + print *, "✗ 有求解器运行失败" + print *, "需要先调试现有代码" + end if + +end program run_eno_weno \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/solver/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03b/src/solver/CMakeLists.txt new file mode 100644 index 00000000..f8499eec --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/solver/CMakeLists.txt @@ -0,0 +1,21 @@ +# src/solver/CMakeLists.txt +message(STATUS "配置求解器模块...") + +add_library(solver STATIC + base.f90 + physics_solver.f90 +) + +target_link_libraries(solver + PRIVATE + infrastructure + core + physics + manager +) + +set_target_properties(solver PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "求解器模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/solver/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/src/solver/base.f90 new file mode 100644 index 00000000..1881ba08 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/solver/base.f90 @@ -0,0 +1,258 @@ +! src/solver/base.f90 +module solver_base_module + use base_modules, only: wp => wp, ip => ip ! 重命名以避免冲突 + + use config_module, only: cfd_config + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create + + implicit none + private + + ! 明确导出列表 + public :: wp, ip ! 类型参数 + public :: solver_base, create_solver_base ! 类型和构造函数 + public :: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, SOLVER_RUNNING + public :: SOLVER_COMPLETED, SOLVER_ERROR ! 状态常量 + + ! 求解器状态枚举 + integer, parameter :: SOLVER_UNINITIALIZED = 0 + integer, parameter :: SOLVER_INITIALIZED = 1 + integer, parameter :: SOLVER_RUNNING = 2 + integer, parameter :: SOLVER_COMPLETED = 3 + integer, parameter :: SOLVER_ERROR = 4 + + ! 求解器基类 + type :: solver_base + ! 基本组件 + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + + ! 状态管理 + integer :: state = SOLVER_UNINITIALIZED + character(len=100) :: error_message = "" + real(wp) :: current_time = 0.0_wp + integer(ip) :: current_step = 0 + + ! 时间控制 + real(wp) :: dt_original = 0.0_wp + contains + procedure :: initialize => solver_base_initialize + procedure :: step => solver_base_step + procedure :: run_to_time => solver_base_run_to_time + procedure :: cleanup => solver_base_cleanup + procedure :: get_state => solver_base_get_state + procedure :: get_error => solver_base_get_error + procedure :: print_info => solver_base_print_info + end type solver_base + + ! 构造函数接口 + interface solver_base + module procedure create_solver_base + end interface + +contains + + ! ==================== 构造函数 ==================== + + function create_solver_base(config, mesh) result(solver) + type(cfd_config), intent(in) :: config + type(mesh_type), intent(in) :: mesh + type(solver_base) :: solver + + solver%config = config + solver%mesh = mesh + + ! 创建域 + solver%domain = domain_create(config, mesh) + + ! 创建解 + solver%solution = solution_create(solver%domain) + + ! 保存原始时间步长 + solver%dt_original = config%dt + + if (config%verbose) then + print *, "[SOLVER] Base solver created" + print *, " Mesh cells: ", mesh%ncells + print *, " Domain total cells: ", solver%domain%ntcells + end if + end function create_solver_base + + ! ==================== 初始化 ==================== + + subroutine solver_base_initialize(this) + class(solver_base), intent(inout) :: this + + if (this%state == SOLVER_INITIALIZED) then + if (this%config%verbose) then + print *, "[SOLVER] Already initialized" + end if + return + end if + + ! 初始化解(通过配置) + ! 这里暂时简化,实际需要调用初始条件工厂 + print *, "[INFO] Base solver initialized (simplified)" + + ! 更新状态 + this%state = SOLVER_INITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + + if (this%config%verbose) then + print *, "[SOLVER] Initialized at t = ", this%current_time + end if + end subroutine solver_base_initialize + + ! ==================== 单步计算(虚方法) ==================== + + subroutine solver_base_step(this, dt) + class(solver_base), intent(inout) :: this + real(wp), intent(in) :: dt + + ! 基类中这只是虚方法,需要在子类中实现 + print *, "[INFO] Base solver step (virtual method)" + print *, " dt = ", dt + print *, " t = ", this%current_time + + ! 更新时间 + this%current_time = this%current_time + dt + this%current_step = this%current_step + 1 + + ! 简单模拟:只是更新状态 + if (this%config%verbose) then + print *, "[SOLVER] Step completed: t = ", this%current_time, & + ", step = ", this%current_step + end if + end subroutine solver_base_step + + ! ==================== 运行到指定时间 ==================== + + subroutine solver_base_run_to_time(this, final_time) + class(solver_base), intent(inout) :: this + real(wp), intent(in) :: final_time + + real(wp) :: dt, t_remaining + integer :: step_count + + if (this%state /= SOLVER_INITIALIZED) then + this%error_message = "Solver not initialized" + this%state = SOLVER_ERROR + return + end if + + this%state = SOLVER_RUNNING + step_count = 0 + + if (this%config%verbose) then + print *, "[SOLVER] Running from t = ", this%current_time, & + " to t = ", final_time + end if + + do while (this%current_time < final_time) + ! 计算时间步长 + dt = min(this%config%dt, final_time - this%current_time) + + ! 执行时间步 + call this%step(dt) + + step_count = step_count + 1 + + ! 每10步输出一次进度 + if (mod(step_count, 10) == 0 .and. this%config%verbose) then + print *, "[SOLVER] Progress: t = ", this%current_time, & + " / ", final_time + end if + end do + + ! 恢复原始时间步长 + this%config%dt = this%dt_original + + ! 更新状态 + this%state = SOLVER_COMPLETED + + if (this%config%verbose) then + print *, "[SOLVER] Run completed:" + print *, " Final time: ", this%current_time + print *, " Total steps: ", this%current_step + end if + end subroutine solver_base_run_to_time + + ! ==================== 清理 ==================== + + subroutine solver_base_cleanup(this) + class(solver_base), intent(inout) :: this + + ! 重置状态 + this%state = SOLVER_UNINITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + this%error_message = "" + + if (this%config%verbose) then + print *, "[SOLVER] Cleaned up" + end if + end subroutine solver_base_cleanup + + ! ==================== 状态查询 ==================== + + function solver_base_get_state(this) result(state) + class(solver_base), intent(in) :: this + integer :: state + state = this%state + end function solver_base_get_state + + function solver_base_get_error(this) result(error_msg) + class(solver_base), intent(in) :: this + character(len=100) :: error_msg + error_msg = trim(this%error_message) + end function solver_base_get_error + + ! ==================== 信息打印 ==================== + + subroutine solver_base_print_info(this) + class(solver_base), intent(in) :: this + + character(len=20) :: state_str + + ! 状态字符串 + select case (this%state) + case (SOLVER_UNINITIALIZED) + state_str = "Uninitialized" + case (SOLVER_INITIALIZED) + state_str = "Initialized" + case (SOLVER_RUNNING) + state_str = "Running" + case (SOLVER_COMPLETED) + state_str = "Completed" + case (SOLVER_ERROR) + state_str = "Error" + case default + state_str = "Unknown" + end select + + print *, "=== Solver Information ===" + print *, "State: ", trim(state_str) + print *, "Current time: ", this%current_time + print *, "Current step: ", this%current_step + print *, "Error message: '", trim(this%error_message), "'" + + ! 配置信息 + print *, "Configuration:" + print *, " Scheme: ", trim(this%config%recon_scheme) + print *, " Order: ", this%config%spatial_order + print *, " dt: ", this%config%dt + + ! 域信息 + print *, "Domain:" + print *, " Ghost layers: ", this%domain%nghosts + print *, " Physical cells: ", this%domain%ist, " to ", this%domain%ied - 1 + + print *, "=========================" + end subroutine solver_base_print_info + +end module solver_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/src/solver/physics_solver.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/src/solver/physics_solver.f90 new file mode 100644 index 00000000..89ee4f40 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/src/solver/physics_solver.f90 @@ -0,0 +1,332 @@ +! src/solver/physics_solver.f90 (修复版) +module physics_solver_module + use base_modules, only: wp => wp, ip => ip + use config_module, only: cfd_config + use mesh_module, only: mesh_type + use solver_base_module, only: solver_base, create_solver_base + use solver_base_module, only: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, & + SOLVER_RUNNING, SOLVER_COMPLETED, SOLVER_ERROR + + use physics_interface, only: physics_equation, physics_problem + use linear_convection_equation, only: linear_convection_eq, create_linear_convection_eq + use linear_convection_problem, only: linear_convection_prob, create_linear_convection_prob + + use component_manager_module, only: create_reconstructor, create_flux_calculator + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + implicit none + private + + ! 明确导出列表 + public :: wp, ip, physics_solver, create_physics_solver + + ! 基于物理的求解器(扩展自solver_base) + type, extends(solver_base) :: physics_solver + ! 物理组件 + class(physics_equation), allocatable :: equation + class(physics_problem), allocatable :: problem + + ! 数值组件 + class(reconstructor_base), allocatable :: reconstructor + class(flux_calculator_base), allocatable :: flux_calculator + + ! 状态信息 + logical :: physics_initialized = .false. + contains + procedure :: initialize => physics_solver_initialize + procedure :: step => physics_solver_step + procedure :: cleanup => physics_solver_cleanup + procedure :: print_info => physics_solver_print_info + procedure :: create_physics_components + procedure :: create_numerical_components + end type physics_solver + + ! 构造函数接口 + interface physics_solver + module procedure create_physics_solver + end interface + +contains + + ! ==================== 构造函数 ==================== + + function create_physics_solver(config, mesh) result(solver) + type(cfd_config), intent(in) :: config + type(mesh_type), intent(in) :: mesh + type(physics_solver) :: solver + + ! 先调用父类构造函数 + solver%solver_base = create_solver_base(config, mesh) + + ! 创建物理组件 + call solver%create_physics_components() + + ! 创建数值组件 + call solver%create_numerical_components() + + if (config%verbose) then + print *, "[PHYSICS SOLVER] Created with physics support" + end if + end function create_physics_solver + + ! ==================== 创建物理组件 ==================== + + subroutine create_physics_components(this) + class(physics_solver), intent(inout) :: this + + ! 检查是否启用物理模块 + if (.not. this%config%enable_physics) then + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Physics module disabled, skipping physics components" + end if + return + end if + + ! 创建物理方程 + select case (trim(this%config%equation_type)) + case ("linear_advection") + allocate(linear_convection_eq :: this%equation) + select type(eq => this%equation) + type is(linear_convection_eq) + eq = create_linear_convection_eq(wave_speed=this%config%wave_speed) + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Created linear convection equation" + print *, " Wave speed: ", eq%wave_speed + end if + end select + + case default + print *, "[WARNING] Unknown equation type: ", trim(this%config%equation_type) + print *, " Using linear convection as default" + + allocate(linear_convection_eq :: this%equation) + select type(eq => this%equation) + type is(linear_convection_eq) + eq = create_linear_convection_eq(wave_speed=this%config%wave_speed) + end select + end select + + ! 创建物理问题 + select case (trim(this%config%problem_type)) + case ("linear_advection") + allocate(linear_convection_prob :: this%problem) + select type(prob => this%problem) + type is(linear_convection_prob) + prob = create_linear_convection_prob( & + wave_speed=this%config%wave_speed, & + domain_length=this%config%domain_length, & + ic_type=this%config%ic_type, & + boundary_type=this%config%boundary_type) + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Created linear convection problem" + print *, " IC type: ", trim(prob%ic_type) + print *, " Domain length: ", prob%domain_length + end if + end select + + case default + print *, "[WARNING] Unknown problem type: ", trim(this%config%problem_type) + print *, " Using linear convection as default" + + allocate(linear_convection_prob :: this%problem) + select type(prob => this%problem) + type is(linear_convection_prob) + prob = create_linear_convection_prob( & + wave_speed=this%config%wave_speed, & + domain_length=this%config%domain_length, & + ic_type=this%config%ic_type, & + boundary_type=this%config%boundary_type) + end select + end select + + this%physics_initialized = .true. + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Physics components created successfully" + end if + end subroutine create_physics_components + + ! ==================== 创建数值组件 ==================== + + subroutine create_numerical_components(this) + class(physics_solver), intent(inout) :: this + integer :: status + + ! 创建重构器 + this%reconstructor = create_reconstructor(this%config, status) + if (status /= 0) then + print *, "[ERROR] Failed to create reconstructor" + this%state = SOLVER_ERROR + this%error_message = "Failed to create reconstructor" + return + end if + + ! 创建通量计算器 + this%flux_calculator = create_flux_calculator(this%config, status) + if (status /= 0) then + print *, "[ERROR] Failed to create flux calculator" + this%state = SOLVER_ERROR + this%error_message = "Failed to create flux calculator" + return + end if + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Numerical components created successfully" + end if + end subroutine create_numerical_components + + ! ==================== 初始化 ==================== + + subroutine physics_solver_initialize(this) + class(physics_solver), intent(inout) :: this + + ! 调用父类初始化 + call this%solver_base%initialize() + + ! 如果启用了物理模块,应用初始条件 + if (this%physics_initialized .and. allocated(this%problem)) then + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Applying initial condition from physics problem" + end if + + select type(prob => this%problem) + type is(linear_convection_prob) + ! 获取网格单元中心坐标 + call prob%initial_condition(this%mesh%xcc, this%solution%u(this%domain%ist:this%domain%ied-1)) + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Initial condition applied" + print *, " u range: ", minval(this%solution%u), " to ", maxval(this%solution%u) + end if + end select + end if + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Initialized with physics support" + end if + end subroutine physics_solver_initialize + + ! ==================== 时间步进(简化实现) ==================== + + subroutine physics_solver_step(this, dt) + class(physics_solver), intent(inout) :: this + real(wp), intent(in) :: dt + + integer :: i, j + real(wp) :: u_val, f_val, residual + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Step with dt = ", dt + end if + + ! 简化的数值方法:直接使用方程计算通量 + if (allocated(this%equation) .and. allocated(this%reconstructor) .and. & + allocated(this%flux_calculator)) then + + ! 这里应该是完整的数值方法: + ! 1. 边界条件 + ! 2. 重构 + ! 3. 通量计算 + ! 4. 残差计算 + ! 5. 时间积分 + + ! 简化的占位实现:只是模拟计算过程 + do i = this%domain%ist, this%domain%ied - 1 + j = i - this%domain%ist + 1 + u_val = this%solution%u(i) + + ! 使用方程计算通量(简化) + select type(eq => this%equation) + type is(linear_convection_eq) + f_val = eq%flux(u_val) + end select + + ! 简化的更新:只是演示 + this%solution%u(i) = u_val - dt * f_val / this%mesh%dx + end do + + ! 更新时间 + this%current_time = this%current_time + dt + this%current_step = this%current_step + 1 + + if (this%config%verbose .and. mod(this%current_step, 10) == 0) then + print *, "[PHYSICS SOLVER] Step ", this%current_step, & + " completed, t = ", this%current_time + end if + + else + ! 如果没有完整的组件,回退到基类方法 + call this%solver_base%step(dt) + end if + end subroutine physics_solver_step + + ! ==================== 清理 ==================== + + subroutine physics_solver_cleanup(this) + class(physics_solver), intent(inout) :: this + + ! 清理物理组件 + if (allocated(this%equation)) deallocate(this%equation) + if (allocated(this%problem)) deallocate(this%problem) + + ! 清理数值组件 + if (allocated(this%reconstructor)) deallocate(this%reconstructor) + if (allocated(this%flux_calculator)) deallocate(this%flux_calculator) + + ! 调用父类清理 + call this%solver_base%cleanup() + + this%physics_initialized = .false. + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Cleaned up physics components" + end if + end subroutine physics_solver_cleanup + + ! ==================== 信息打印 ==================== + + subroutine physics_solver_print_info(this) + class(physics_solver), intent(in) :: this + + ! 调用父类信息打印 + call this%solver_base%print_info() + + ! 添加物理信息 + print *, "=== Physics Information ===" + print *, "Physics initialized: ", this%physics_initialized + + if (allocated(this%equation)) then + print *, "Equation: allocated" + select type(eq => this%equation) + type is(linear_convection_eq) + print *, " Type: Linear Convection" + print *, " Wave speed: ", eq%wave_speed + class default + print *, " Type: Unknown" + end select + else + print *, "Equation: not allocated" + end if + + if (allocated(this%problem)) then + print *, "Problem: allocated" + select type(prob => this%problem) + type is(linear_convection_prob) + print *, " Type: Linear Convection Problem" + print *, " IC type: ", trim(prob%ic_type) + print *, " Domain length: ", prob%domain_length + class default + print *, " Type: Unknown" + end select + else + print *, "Problem: not allocated" + end if + + print *, "Numerical components:" + print *, " Reconstructor: ", allocated(this%reconstructor) + print *, " Flux calculator: ", allocated(this%flux_calculator) + print *, "==========================" + end subroutine physics_solver_print_info + +end module physics_solver_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/CMakeLists.txt new file mode 100644 index 00000000..aabc1c7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/CMakeLists.txt @@ -0,0 +1,95 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +# 基础设施测试 +add_executable(test_infrastructure test_infrastructure.f90) +target_link_libraries(test_infrastructure + PRIVATE + infrastructure + core +) + +# 注册系统测试 +add_executable(test_registry test_registry.f90) +target_link_libraries(test_registry + PRIVATE + core + infrastructure +) + +# 物理模块测试 +add_executable(test_physics test_physics.f90) +target_link_libraries(test_physics + PRIVATE + physics + base +) + +# 组件管理器测试 +add_executable(test_component_manager test_component_manager.f90) +target_link_libraries(test_component_manager + PRIVATE + manager + infrastructure +) + +# 配置物理测试 +add_executable(test_config_physics test_config_physics.f90) +target_link_libraries(test_config_physics + PRIVATE + infrastructure + core +) + +# 求解器基础测试 +add_executable(test_solver_base test_solver_base.f90) +target_link_libraries(test_solver_base + PRIVATE + solver + infrastructure + core +) + +# 物理求解器测试 +add_executable(test_physics_solver test_physics_solver.f90) +target_link_libraries(test_physics_solver + PRIVATE + solver + infrastructure + core + physics + manager +) + +add_executable(test_simple_link test_simple_link.f90) +target_link_libraries(test_simple_link + PRIVATE + reconstructor + flux +) + + +add_executable(test_factory_simple test_factory_simple.f90) +target_link_libraries(test_factory_simple + PRIVATE + core + infrastructure + reconstructor + flux +) + +add_executable(test_domain_solution test_domain_solution.f90) +target_link_libraries(test_domain_solution + PRIVATE + infrastructure + core +) + +add_executable(test_component_manager_physics test_component_manager_physics.f90) +target_link_libraries(test_component_manager_physics + PRIVATE + manager + infrastructure + physics + core +) diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_cfd_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_cfd_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_cfd_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_component_manager.f90 new file mode 100644 index 00000000..f60c3505 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_component_manager.f90 @@ -0,0 +1,111 @@ +! tests/test_component_manager.f90 +program test_component_manager + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use component_manager_module, only: create_reconstructor, create_flux_calculator + use component_manager_module, only: component_manager_info, validate_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + implicit none + + type(cfd_config) :: config + class(reconstructor_base), allocatable :: recon + class(flux_calculator_base), allocatable :: flux + integer :: status + logical :: is_valid + + print *, "=== Component Manager Test ===" + print *, "" + + ! 显示组件管理器信息 + call component_manager_info() + print *, "" + + ! 测试1: 基本配置 + print *, "1. Testing basic ENO3 + Rusanov configuration..." + print *, "-----------------------------------------------" + + config%verbose = .true. + call config_print(config) + + ! 配置ENO3重构 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + call config_print(config) + print *, "" + + ! 验证配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Configuration is valid" + else + print *, "[ERROR] Configuration is invalid" + end if + print *, "" + + ! 测试2: 创建组件 + print *, "2. Testing component creation..." + print *, "--------------------------------" + + ! 创建重构器(带状态检查) + recon = create_reconstructor(config, status) + if (status == 0) then + print *, "[OK] Reconstructor created successfully" + call recon%info() + else + print *, "[ERROR] Failed to create reconstructor, code:", status + end if + print *, "" + + ! 创建通量计算器 + flux = create_flux_calculator(config, status) + if (status == 0) then + print *, "[OK] Flux calculator created successfully" + call flux%info() + else + print *, "[ERROR] Failed to create flux calculator, code:", status + end if + print *, "" + + ! 测试3: WENO3重构测试 + print *, "3. Testing WENO3 configuration..." + print *, "---------------------------------" + + call config_with_reconstruction(config, "weno3", 3) + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] WENO3 configuration is valid" + + ! 创建WENO3重构器 + recon = create_reconstructor(config) + call recon%info() + else + print *, "[ERROR] WENO3 configuration is invalid" + end if + print *, "" + + ! 测试4: 错误配置测试 + print *, "4. Testing invalid configuration..." + print *, "-----------------------------------" + + config%recon_scheme = "unknown_scheme" + config%flux_type = "unknown_flux" + + is_valid = validate_config(config) + if (.not. is_valid) then + print *, "[OK] Invalid configuration correctly rejected" + else + print *, "[ERROR] Invalid configuration should have been rejected" + end if + + ! 清理 + if (allocated(recon)) deallocate(recon) + if (allocated(flux)) deallocate(flux) + + print *, "" + print *, "=== Component manager test completed successfully ===" + +end program test_component_manager \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_component_manager_physics.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_component_manager_physics.f90 new file mode 100644 index 00000000..f2becca9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_component_manager_physics.f90 @@ -0,0 +1,120 @@ +! tests/test_component_manager_physics.f90 (简化版) +program test_component_manager_physics + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + use component_manager_module, only: component_manager_info, validate_config + + implicit none + + type(cfd_config) :: config + logical :: is_valid + + print *, "=== Component Manager Physics Test (Simplified) ===" + print *, "" + + ! 测试1: 显示组件管理器信息 + print *, "1. Testing component manager info..." + print *, "-------------------------------------" + call component_manager_info() + print *, "" + + ! 测试2: 物理模块测试(默认) + print *, "2. Testing physics module with default configuration..." + print *, "------------------------------------------------------" + + config%verbose = .true. + call config_print(config) + + ! 验证配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Default configuration is valid" + else + print *, "[ERROR] Default configuration is invalid" + end if + print *, "" + + ! 测试3: 测试物理配置 + print *, "3. Testing physics configuration..." + print *, "------------------------------------" + + ! 修改物理参数 + config%equation_type = "linear_advection" + config%problem_type = "linear_advection" + config%wave_speed = 2.5_wp + config%domain_length = 3.0_wp + + print *, "Modified physics configuration:" + print *, " Equation type: ", trim(config%equation_type) + print *, " Problem type: ", trim(config%problem_type) + print *, " Wave speed: ", config%wave_speed + print *, " Domain length: ", config%domain_length + print *, " Physics enabled: ", config%enable_physics + + ! 验证修改后的配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Modified physics configuration is valid" + else + print *, "[ERROR] Modified physics configuration is invalid" + end if + print *, "" + + ! 测试4: 数值组件测试 + print *, "4. Testing numerical components with physics..." + print *, "-----------------------------------------------" + + call config_with_reconstruction(config, "weno3", 3) + config%flux_type = "rusanov" + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Combined physics+numerics configuration is valid" + else + print *, "[ERROR] Combined configuration is invalid" + end if + print *, "" + + ! 测试5: 物理模块禁用测试 + print *, "5. Testing physics module disabled..." + print *, "---------------------------------------" + + config%enable_physics = .false. + config%verbose = .false. + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Configuration valid even with physics disabled" + else + print *, "[ERROR] Configuration should be valid with physics disabled" + end if + print *, "" + + ! 测试6: 错误配置测试 + print *, "6. Testing error handling..." + print *, "-----------------------------" + + config%verbose = .true. + config%enable_physics = .true. + config%recon_scheme = "unknown_scheme" + config%flux_type = "unknown_flux" + config%equation_type = "unknown_equation" + config%problem_type = "unknown_problem" + + is_valid = validate_config(config) + if (.not. is_valid) then + print *, "[OK] Invalid configuration correctly rejected" + else + print *, "[ERROR] Invalid configuration should have been rejected" + end if + print *, "" + + print *, "=== Component Manager Physics Test Summary ===" + print *, "✓ Component manager info works" + print *, "✓ Configuration validation works with physics" + print *, "✓ Error handling works correctly" + print *, "✓ Combined physics+numerics validation works" + print *, "" + print *, "下一步: 集成物理模块到求解器框架" + +end program test_component_manager_physics \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_config_physics.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_config_physics.f90 new file mode 100644 index 00000000..c6fef5c0 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_config_physics.f90 @@ -0,0 +1,141 @@ +! tests/test_config_physics.f90 (修复版) +program test_config_physics + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + + implicit none + + type(cfd_config) :: config + + print *, "=== Configuration Physics Test (Simplified) ===" + print *, "" + + ! 测试1: 默认配置 + print *, "1. Testing default configuration..." + print *, "-----------------------------------" + call config_print(config) + print *, "" + + ! 测试2: 验证基础物理字段 + print *, "2. Testing basic physics fields..." + print *, "----------------------------------" + + print *, "Verifying default physics fields:" + + if (trim(config%equation_type) == "linear_advection") then + print *, " ✓ Default equation type: linear_advection" + else + print *, " ✗ Unexpected equation type: ", trim(config%equation_type) + end if + + if (trim(config%problem_type) == "linear_advection") then + print *, " ✓ Default problem type: linear_advection" + else + print *, " ✗ Unexpected problem type: ", trim(config%problem_type) + end if + + if (abs(config%domain_length - 2.0_wp) < 1e-10_wp) then + print *, " ✓ Default domain length: 2.0" + else + print *, " ✗ Unexpected domain length: ", config%domain_length + end if + + if (config%enable_physics) then + print *, " ✓ Physics enabled by default" + else + print *, " ✗ Physics not enabled by default" + end if + + print *, "" + + ! 测试3: 使用类型绑定的方法(正确的方法名) + print *, "3. Testing type-bound procedures..." + print *, "--------------------------------------" + + call config%set_physics_parameters( & + equation_type="burgers_equation", & + problem_type="sod_shock_tube", & + domain_length=3.0_wp, & + enable_physics=.false.) + + print *, "After set_physics_parameters:" + print *, " Equation type: ", trim(config%equation_type) + print *, " Problem type: ", trim(config%problem_type) + print *, " Domain length: ", config%domain_length + print *, " Physics enabled: ", config%enable_physics + + if (trim(config%equation_type) == "burgers_equation") then + print *, " ✓ Equation type modified successfully via set_physics_parameters" + end if + + if (trim(config%problem_type) == "sod_shock_tube") then + print *, " ✓ Problem type modified successfully via set_physics_parameters" + end if + + if (abs(config%domain_length - 3.0_wp) < 1e-10_wp) then + print *, " ✓ Domain length modified successfully via set_physics_parameters" + end if + + if (.not. config%enable_physics) then + print *, " ✓ Physics disabled successfully via set_physics_parameters" + end if + + print *, "" + + ! 测试4: 调用get_physics_info方法 + print *, "4. Testing get_physics_info method..." + print *, "--------------------------------------" + call config%get_physics_info() + print *, "" + + ! 测试5: 高斯脉冲配置 + print *, "5. Testing Gaussian pulse configuration..." + print *, "-----------------------------------------" + + config%ic_type = "gaussian" + config%pulse_center = 0.6_wp + config%pulse_width = 0.15_wp + + print *, "Gaussian pulse parameters:" + print *, " IC type: ", trim(config%ic_type) + print *, " Center: ", config%pulse_center + print *, " Width: ", config%pulse_width + + if (trim(config%ic_type) == "gaussian") then + print *, " ✓ Gaussian IC type set" + end if + + if (abs(config%pulse_center - 0.6_wp) < 1e-10_wp) then + print *, " ✓ Pulse center set" + end if + + if (abs(config%pulse_width - 0.15_wp) < 1e-10_wp) then + print *, " ✓ Pulse width set" + end if + + print *, "" + + ! 测试6: 重构配置 + print *, "6. Testing reconstruction configuration..." + print *, "------------------------------------------" + + call config_with_reconstruction(config, "weno", 5) + + print *, "Reconstruction configuration:" + print *, " Scheme: ", trim(config%recon_scheme) + print *, " Order: ", config%spatial_order + + if (trim(config%recon_scheme) == "weno" .and. config%spatial_order == 5) then + print *, " ✓ WENO5 configuration successful" + else + print *, " ✗ Reconstruction configuration failed" + end if + + print *, "" + + print *, "=== Configuration Physics Test Complete ===" + print *, "✓ Config module updated with physics support" + print *, "✓ Fields can be directly accessed and modified" + print *, "✓ Type-bound procedures work correctly" + +end program test_config_physics \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_domain_solution.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_domain_solution.f90 new file mode 100644 index 00000000..ff659bac --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_domain_solution.f90 @@ -0,0 +1,102 @@ +! tests/test_domain_solution.f90 +program test_domain_solution + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create, solution_reset + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + real(wp), allocatable :: initial_values(:) + integer :: i + + print *, "=== Domain and Solution Test ===" + print *, "" + + ! 测试1: 不同重构方案的ghost层计算 + print *, "1. Testing ghost layer calculation..." + print *, "--------------------------------------" + + ! ENO3 + call config_with_reconstruction(config, "eno", 3) + config%verbose = .false. + call mesh%init(ncells=10) + domain = domain_create(config, mesh) + print *, "ENO3: nghosts = ", domain%nghosts, " (expected: 3)" + + ! WENO3 + call config_with_reconstruction(config, "weno3", 3) + domain = domain_create(config, mesh) + print *, "WENO3: nghosts = ", domain%nghosts, " (expected: 2)" + + ! WENO5 + call config_with_reconstruction(config, "weno", 5) + domain = domain_create(config, mesh) + print *, "WENO5: nghosts = ", domain%nghosts, " (expected: 3)" + print *, "" + + ! 测试2: Solution数组 + print *, "2. Testing solution arrays..." + print *, "------------------------------" + + call config_with_reconstruction(config, "eno", 3) + config%verbose = .true. + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + domain = domain_create(config, mesh) + call domain%print_info() + print *, "" + + solution = solution_create(domain) + call solution%print_info() + print *, "" + + ! 测试3: 初始化和更新 + print *, "3. Testing initialization and update..." + print *, "----------------------------------------" + + allocate(initial_values(mesh%ncells)) + do i = 1, mesh%ncells + initial_values(i) = sin(2.0_wp * 3.14159265358979_wp * mesh%xcc(i) / mesh%L) + end do + + call solution%initialize(initial_values) + print *, "After initialization:" + print *, " u range: ", minval(solution%u), " to ", maxval(solution%u) + print *, " un range: ", minval(solution%un), " to ", maxval(solution%un) + + ! 修改当前解,测试更新 + solution%u = solution%u * 2.0_wp + call solution%update_old_field() + print *, "After update: max|u - un| = ", maxval(abs(solution%u - solution%un)) + print *, "" + + ! 测试4: 重置 + print *, "4. Testing reset..." + print *, "-------------------" + + call solution_reset(solution) + print *, "After reset:" + print *, " u max: ", maxval(abs(solution%u)) + print *, " un max: ", maxval(abs(solution%un)) + print *, " flux max: ", maxval(abs(solution%flux)) + print *, "" + + deallocate(initial_values) + + print *, "=== Test Summary ===" + print *, "✓ Ghost layer calculation works" + print *, "✓ Domain creation works" + print *, "✓ Solution arrays work" + print *, "✓ Initialization works" + print *, "✓ Field update works" + print *, "✓ Reset works" + print *, "" + print *, "Ready for next step: Implementing Physics modules" + +end program test_domain_solution \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_factory_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_factory_simple.f90 new file mode 100644 index 00000000..db65da7c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_factory_simple.f90 @@ -0,0 +1,58 @@ +! tests/test_factory_simple.f90 (修复版) +program test_factory_simple + use base_modules, only: wp ! ← 添加这行 + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Basic systems + print *, "1. Testing basic systems..." + print *, "-----------------------------" + call config_print(config) + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Creating reconstructors + print *, "2. Testing reconstructors..." + print *, "------------------------------" + + ! 创建并测试ENO重构器 + print *, "Creating ENO reconstructor..." + eno = eno_reconstructor() ! 使用构造函数 + call eno%info() ! 必须调用info方法 + + print *, "" + print *, "Creating WENO3 reconstructor..." + weno3 = weno3_reconstructor() ! 使用构造函数 + call weno3%info() ! 必须调用info方法 + print *, "" + + ! Test 3: Creating flux calculator + print *, "3. Testing flux calculator..." + print *, "-------------------------------" + + print *, "Creating Rusanov flux calculator..." + rusanov = rusanov_flux() ! 使用构造函数 + call rusanov%info() ! 必须调用info方法 + print *, "" + + print *, "=== Factory pattern simple test completed successfully ===" + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_infrastructure.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_infrastructure.f90 new file mode 100644 index 00000000..22fa92d1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_infrastructure.f90 @@ -0,0 +1,56 @@ +! tests/test_infrastructure.f90 (原test_basic_only.f90) +program test_infrastructure + use base_modules, only: wp + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use registry_module, only: registry_init, registry_cleanup, & + register_component_simple, list_components + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "=== 基础设施测试 ===" + print *, "" + + ! 测试1: 配置 + print *, "1. 测试配置模块..." + print *, "-------------------" + call config_print(config) + print *, "" + + ! 测试2: 网格 + print *, "2. 测试网格模块..." + print *, "------------------" + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=5) + print *, "网格初始化:" + print *, " 单元数: ", mesh%ncells + print *, " 节点数: ", mesh%nnodes + print *, " 网格间距: ", mesh%dx + print *, "" + + ! 测试3: 注册系统 + print *, "3. 测试注册系统..." + print *, "------------------" + + call registry_init() + + ! 注册组件(使用简化版本) + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! 列出组件 + call list_components() + print *, "" + + ! 清理 + call registry_cleanup() + + print *, "=== 基础设施测试通过 ===" + print *, "✓ 配置模块工作正常" + print *, "✓ 网格模块工作正常" + print *, "✓ 注册系统工作正常" + +end program test_infrastructure \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_physics.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_physics.f90 new file mode 100644 index 00000000..3dababb6 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_physics.f90 @@ -0,0 +1,91 @@ +! tests/test_physics.f90 (原test_physics_minimal.f90) +program test_physics + use base_modules, only: wp, ip + use linear_convection_equation, only: linear_convection_eq, create_linear_convection_eq + use linear_convection_problem, only: linear_convection_prob, create_linear_convection_prob + + implicit none + + type(linear_convection_eq) :: eq + type(linear_convection_prob) :: prob + real(wp) :: u, f, a + real(wp), allocatable :: x(:), u_ic(:), u_exact(:) + integer :: i, nx = 10 + + print *, "=== 物理模块基础测试 ===" + print *, "" + + ! 测试1: 方程功能 + print *, "1. 测试方程功能..." + print *, "-------------------" + + eq = create_linear_convection_eq(wave_speed=2.0_wp) + print *, "方程: ", eq%name + print *, "波速: ", eq%wave_speed + + u = 1.5_wp + f = eq%flux(u) + a = eq%speed() + + print *, "u = ", u + print *, "F(u) = ", f, " (期望: 3.0)" + print *, "波速 a = ", a, " (期望: 2.0)" + + if (abs(f - 3.0_wp) < 1e-10_wp .and. abs(a - 2.0_wp) < 1e-10_wp) then + print *, "✓ 方程功能正常" + else + print *, "✗ 方程功能异常" + end if + print *, "" + + ! 测试2: 问题功能 + print *, "2. 测试问题功能..." + print *, "-------------------" + + prob = create_linear_convection_prob(ic_type="step", domain_length=2.0_wp) + print *, "问题: ", prob%name + print *, "初始条件类型: ", trim(prob%ic_type) + print *, "域长度: ", prob%domain_length + + allocate(x(nx), u_ic(nx), u_exact(nx)) + do i = 1, nx + x(i) = 0.0_wp + (i-1) * 0.2_wp + end do + + ! 测试初始条件 + call prob%initial_condition(x, u_ic) + print *, "初始条件范围: ", minval(u_ic), " 到 ", maxval(u_ic) + + ! 测试精确解 + u_exact = prob%exact_solution(x, 0.0_wp) + print *, "t=0时精确解范围: ", minval(u_exact), " 到 ", maxval(u_exact) + + ! 检查阶跃函数 + if (abs(u_ic(1) - 1.0_wp) < 1e-10_wp .and. & + abs(u_ic(6) - 2.0_wp) < 1e-10_wp) then + print *, "✓ 阶跃初始条件正确" + else + print *, "✗ 阶跃初始条件错误" + end if + + ! 检查精确解与初始条件一致 + if (maxval(abs(u_ic - u_exact)) < 1e-10_wp) then + print *, "✓ t=0时精确解与初始条件一致" + else + print *, "✗ 精确解计算错误" + end if + print *, "" + + ! 测试3: 边界条件接口 + print *, "3. 测试边界条件接口..." + print *, "----------------------" + call prob%boundary_condition(u_ic, 0.0_wp) + print *, "✓ 边界条件接口正常" + print *, "" + + deallocate(x, u_ic, u_exact) + + print *, "=== 物理模块测试完成 ===" + print *, "下一步: 将物理模块集成到现有系统中" + +end program test_physics \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_physics_solver.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_physics_solver.f90 new file mode 100644 index 00000000..ba49ffba --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_physics_solver.f90 @@ -0,0 +1,133 @@ +! tests/test_physics_solver.f90 (修复版) +program test_physics_solver + use base_modules, only: wp ! 使用一致的wp定义 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use mesh_module, only: mesh_type + use solver_base_module, only: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, & + SOLVER_COMPLETED, SOLVER_ERROR + use physics_solver_module, only: physics_solver + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(physics_solver) :: psolver + character(len=100) :: error_msg + integer :: state + + print *, "=== Physics Solver Test ===" + print *, "" + + ! 测试1: 创建物理求解器(默认物理配置) + print *, "1. Creating physics solver (default physics)..." + print *, "------------------------------------------------" + + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%dt = 0.01_wp + config%enable_physics = .true. + + call config_print(config) + + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + psolver = physics_solver(config, mesh) + call psolver%print_info() + print *, "" + + ! 测试2: 初始化 + print *, "2. Initializing physics solver..." + print *, "----------------------------------" + + call psolver%initialize() + state = psolver%get_state() + error_msg = psolver%get_error() + print *, "State after initialization: ", state + print *, "Expected: ", SOLVER_INITIALIZED + print *, "Match? ", state == SOLVER_INITIALIZED + print *, "Error message: '", trim(error_msg), "'" + print *, "" + + ! 测试3: 运行一小段时间 + print *, "3. Running physics solver (short time)..." + print *, "------------------------------------------" + + call psolver%run_to_time(0.02_wp) + state = psolver%get_state() + error_msg = psolver%get_error() + print *, "State after short run: ", state + print *, "Expected: ", SOLVER_COMPLETED + print *, "Match? ", state == SOLVER_COMPLETED + print *, "Current time: ", psolver%current_time + print *, "Current step: ", psolver%current_step + print *, "" + + ! 测试4: 禁用物理模块 + print *, "4. Testing physics solver with physics disabled..." + print *, "--------------------------------------------------" + + config%enable_physics = .false. + config%verbose = .false. + + psolver = physics_solver(config, mesh) + call psolver%initialize() + call psolver%run_to_time(0.01_wp) + + state = psolver%get_state() + error_msg = psolver%get_error() + print *, "State with physics disabled: ", state + print *, "Expected: ", SOLVER_COMPLETED + print *, "Match? ", state == SOLVER_COMPLETED + print *, "" + + ! 测试5: 不同物理配置 + print *, "5. Testing different physics configurations..." + print *, "----------------------------------------------" + + config%verbose = .true. + config%enable_physics = .true. + config%equation_type = "linear_advection" + config%problem_type = "linear_advection" + config%wave_speed = 2.5_wp + config%domain_length = 3.0_wp + config%ic_type = "gaussian" + + psolver = physics_solver(config, mesh) + call psolver%initialize() + + print *, "Physics configuration test completed" + print *, "" + + ! 测试6: 清理和错误处理 + print *, "6. Testing cleanup and error handling..." + print *, "----------------------------------------" + + call psolver%cleanup() + state = psolver%get_state() + error_msg = psolver%get_error() + print *, "State after cleanup: ", state + print *, "Expected: ", SOLVER_UNINITIALIZED + print *, "Match? ", state == SOLVER_UNINITIALIZED + + ! 尝试运行已清理的求解器 + call psolver%run_to_time(0.01_wp) + state = psolver%get_state() + error_msg = psolver%get_error() + print *, "State after error attempt: ", state + print *, "Expected: ", SOLVER_ERROR + print *, "Match? ", state == SOLVER_ERROR + print *, "Error message: '", trim(error_msg), "'" + print *, "" + + ! 最终信息 + print *, "=== Physics Solver Test Complete ===" + print *, "✓ Physics solver creation works" + print *, "✓ Physics component initialization works" + print *, "✓ Physics-enabled time stepping works" + print *, "✓ Physics disabled mode works" + print *, "✓ Different physics configurations work" + print *, "✓ Cleanup and error handling work" + print *, "" + print *, "下一步: 实现完整的数值方法(重构、通量、时间积分)" + +end program test_physics_solver \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_registry.f90 new file mode 100644 index 00000000..e82651ff --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_registry.f90 @@ -0,0 +1,87 @@ +! tests/test_registry.f90 (原test_minimal_simple.f90) +program test_registry + use base_modules, only: wp + use registry_module + use config_module + use mesh_module + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== 注册系统功能测试 ===" + print *, "" + + ! 测试1: 配置系统 + print *, "1. 测试配置系统" + print *, "--------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! 测试2: 网格系统 + print *, "2. 测试网格系统" + print *, "--------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! 测试3: 注册系统 + print *, "3. 测试注册系统" + print *, "--------------" + + call registry_init() + + ! 注册组件 + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call list_components() + print *, "注册表大小: ", registry_get_size() + print *, "" + + ! 测试组件查找 + print *, "4. 测试组件查找" + print *, "--------------" + + if (has_component_simple("reconstructor", "eno")) then + print *, "找到: reconstructor.eno" + else + print *, "未找到: reconstructor.eno" + end if + + if (has_component_simple("reconstructor", "unknown")) then + print *, "找到: reconstructor.unknown" + else + print *, "未找到: reconstructor.unknown" + end if + print *, "" + + ! 测试获取可用组件 + print *, "5. 测试注册系统功能" + print *, "------------------" + print *, "注册表已初始化: ", registry_is_initialized() + print *, "组件数量: ", registry_get_size() + print *, "" + + ! 清理 + call registry_cleanup() + + print *, "=== 注册系统测试完成 ===" + +end program test_registry \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_simple_link.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_simple_link.f90 new file mode 100644 index 00000000..71cc614e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_simple_link.f90 @@ -0,0 +1,78 @@ +! tests/test_simple_link.f90 +program test_simple_link + use base_modules, only: wp ! ← 添加这行 + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call registry_init() + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call list_components() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component_simple("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component_simple("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Cleanup + call registry_cleanup() + + print *, "=== Minimal test completed successfully ===" + +end program test_simple_link \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_solver_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_solver_base.f90 new file mode 100644 index 00000000..6cfe47e4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_solver_base.f90 @@ -0,0 +1,99 @@ +! tests/test_solver_base.f90 (修复版) +program test_solver_base + ! 所有 USE 语句必须在程序开始处 + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + use mesh_module, only: mesh_type + use solver_base_module, only: solver_base, SOLVER_UNINITIALIZED, & + SOLVER_INITIALIZED, SOLVER_COMPLETED, SOLVER_ERROR + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(solver_base) :: solver + integer :: state + + print *, "=== Solver Base Test ===" + print *, "" + + ! 测试1: 创建求解器 + print *, "1. Creating solver..." + print *, "----------------------" + + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%dt = 0.01_wp + + call config_print(config) + + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + solver = solver_base(config, mesh) + call solver%print_info() + print *, "" + + ! 测试2: 初始化 + print *, "2. Initializing solver..." + print *, "-------------------------" + + call solver%initialize() + state = solver%get_state() + print *, "State after initialization: ", state + print *, "Expected: ", SOLVER_INITIALIZED + print *, "Match? ", state == SOLVER_INITIALIZED + print *, "Error message: '", trim(solver%get_error()), "'" + print *, "" + + ! 测试3: 运行求解器 + print *, "3. Running solver..." + print *, "--------------------" + + call solver%run_to_time(0.05_wp) + state = solver%get_state() + print *, "State after run: ", state + print *, "Expected: ", SOLVER_COMPLETED + print *, "Match? ", state == SOLVER_COMPLETED + print *, "Current time: ", solver%current_time + print *, "Current step: ", solver%current_step + print *, "" + + ! 测试4: 再次运行(从已完成状态) + print *, "4. Running again from completed state..." + print *, "----------------------------------------" + + ! 需要先清理才能重新运行 + call solver%cleanup() + call solver%initialize() + call solver%run_to_time(0.1_wp) + + call solver%print_info() + print *, "" + + ! 测试5: 错误处理 + print *, "5. Testing error states..." + print *, "--------------------------" + + ! 创建一个未初始化的求解器 + call solver%cleanup() + state = solver%get_state() + print *, "Uninitialized state: ", state + print *, "Expected: ", SOLVER_UNINITIALIZED + print *, "Match? ", state == SOLVER_UNINITIALIZED + + ! 尝试运行未初始化的求解器 + call solver%run_to_time(0.01_wp) + state = solver%get_state() + print *, "State after error: ", state + print *, "Expected: ", SOLVER_ERROR + print *, "Match? ", state == SOLVER_ERROR + print *, "Error message: '", trim(solver%get_error()), "'" + print *, "" + + print *, "=== Solver Base Test Complete ===" + print *, "✓ Solver base class works" + print *, "✓ State management works" + print *, "✓ Time stepping framework works" + print *, "✓ Error handling works" + +end program test_solver_base \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_solver_framework.f90 b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_solver_framework.f90 new file mode 100644 index 00000000..6754323d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03b/tests/test_solver_framework.f90 @@ -0,0 +1,91 @@ +! tests/test_solver_framework.f90 +program test_solver_framework + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use mesh_module, only: mesh_type + use solver_module, only: cfd_solver, solver_create, solver_run, solver_cleanup + use solver_module, only: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, & + SOLVER_COMPLETED, SOLVER_ERROR + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(cfd_solver) :: solver + + print *, "=== 求解器框架测试 ===" + print *, "" + + ! 测试1: 基本创建 + print *, "1. 测试求解器创建..." + print *, "----------------------" + + ! 创建配置 + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.0_real64 + config%dt = 0.01_real64 + + call config_print(config) + print *, "" + + ! 创建网格 + call mesh%init(xmin=0.0_real64, xmax=2.0_real64, ncells=20) + call mesh%print_info() + print *, "" + + ! 创建求解器 + solver = solver_create(config, mesh) + print *, "✓ 求解器创建成功" + print *, " 状态: ", solver%get_state() + print *, "" + + ! 测试2: 求解器初始化 + print *, "2. 测试求解器初始化..." + print *, "------------------------" + + call solver%initialize() + print *, "✓ 求解器初始化完成" + print *, " 状态: ", solver%get_state() + print *, " 错误信息: '", trim(solver%get_error()), "'" + print *, "" + + ! 测试3: 简单运行 + print *, "3. 测试求解器运行..." + print *, "----------------------" + + call solver_run(solver, 0.05_real64) ! 运行到0.05秒 + print *, "✓ 求解器运行完成" + print *, " 最终状态: ", solver%get_state() + print *, "" + + ! 测试4: 清理 + print *, "4. 测试求解器清理..." + print *, "----------------------" + + call solver_cleanup(solver) + print *, "✓ 求解器清理完成" + print *, " 状态: ", solver%get_state() + print *, "" + + ! 测试5: 错误处理 + print *, "5. 测试错误处理..." + print *, "-------------------" + + ! 尝试重复初始化 + call solver%initialize() + print *, " 重复初始化状态: ", solver%get_state() + print *, " 错误信息: '", trim(solver%get_error()), "'" + + call solver_cleanup(solver) + print *, "" + + print *, "=== 框架测试总结 ===" + print *, "✓ 求解器创建/初始化/运行/清理流程验证完成" + print *, "✓ 状态管理正常工作" + print *, "✓ 错误处理机制就绪" + print *, "" + print *, "下一步: 添加实际数值计算功能" + +end program test_solver_framework \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03c/CMakeLists.txt new file mode 100644 index 00000000..55859dc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) +add_subdirectory(examples) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/README.md b/example/1d-linear-convection/weno3/fortran/registry/03c/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/examples/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03c/examples/CMakeLists.txt new file mode 100644 index 00000000..9207e0f4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/examples/CMakeLists.txt @@ -0,0 +1,22 @@ +# examples/CMakeLists.txt +message(STATUS "配置示例程序...") + +# 主示例程序:ENO/WENO对比 +add_executable(example_eno_weno_comparison + run_eno_weno.f90 +) + +target_link_libraries(example_eno_weno_comparison + PRIVATE + solver + infrastructure + core + physics + manager +) + +install(TARGETS example_eno_weno_comparison + RUNTIME DESTINATION bin/examples +) + +message(STATUS "示例程序配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/examples/run_eno_weno.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/examples/run_eno_weno.f90 new file mode 100644 index 00000000..96162038 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/examples/run_eno_weno.f90 @@ -0,0 +1,359 @@ +! examples/run_eno_weno.f90 (修复版) +program run_eno_weno + ! 示例程序:ENO/WENO对比分析 + + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction, config_print + use mesh_module, only: mesh_type + use registry_module, only: registry_init, registry_cleanup, initialize_default_components + use physics_solver_module, only: physics_solver, SOLVER_INITIALIZED, & + SOLVER_COMPLETED, SOLVER_ERROR + + implicit none + + ! 求解器实例 + type(cfd_config) :: config_eno3, config_weno3, config_weno5 + type(mesh_type) :: mesh + type(physics_solver) :: solver_eno3, solver_weno3, solver_weno5 + + ! 保存结果的变量 + real(wp) :: time_eno3, time_weno3, time_weno5 + integer :: steps_eno3, steps_weno3, steps_weno5 + integer :: state_eno3, state_weno3, state_weno5 + character(len=100) :: error_eno3, error_weno3, error_weno5 + + character(len=100) :: status_str + logical :: all_success + + print *, "==========================================" + print *, " ENO/WENO对比分析 - Fortran版本" + print *, "==========================================" + print *, "" + + ! 步骤0: 系统初始化 + print *, "[步骤0] 初始化系统..." + print *, "---------------------" + + ! 初始化注册系统 + call registry_init(verbose=.true.) + + ! 注册默认组件 + print *, "注册默认组件..." + call initialize_default_components() + print *, "" + + ! 步骤1: 创建网格 + print *, "[步骤1] 创建计算网格..." + print *, "------------------------" + + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=40) + call mesh%print_info() + print *, "" + + ! ========== ENO3 求解器 ========== + print *, "[步骤2] 配置并运行ENO3求解器..." + print *, "--------------------------------" + + ! 配置ENO3 + config_eno3%verbose = .true. + config_eno3%ic_type = "step" + config_eno3%wave_speed = 1.0_wp + config_eno3%final_time = 0.625_wp + config_eno3%dt = 0.0025_wp + config_eno3%rk_order = 2 + config_eno3%boundary_type = "periodic" + config_eno3%equation_type = "linear_advection" + config_eno3%problem_type = "linear_advection" + config_eno3%enable_physics = .true. + config_eno3%domain_length = 2.0_wp + + call config_with_reconstruction(config_eno3, "eno", 3) + + print *, "ENO3配置信息:" + call config_print(config_eno3) + print *, "" + + ! 创建并运行ENO3求解器 + print *, "创建ENO3求解器实例..." + solver_eno3 = physics_solver(config_eno3, mesh) + + print *, "初始化ENO3求解器..." + call solver_eno3%initialize() + + print *, "运行ENO3求解器..." + call solver_eno3%run_to_time(config_eno3%final_time) + + ! 立即保存ENO3结果 + state_eno3 = solver_eno3%get_state() + time_eno3 = solver_eno3%current_time + steps_eno3 = solver_eno3%current_step + error_eno3 = solver_eno3%get_error() + + print *, "检查ENO3求解器状态..." + if (state_eno3 == SOLVER_COMPLETED) then + print *, "✓ ENO3求解器运行完成" + print *, " 最终时间: ", time_eno3 + print *, " 总步数: ", steps_eno3 + else if (state_eno3 == SOLVER_ERROR) then + print *, "✗ ENO3求解器运行失败" + print *, " 错误信息: ", trim(error_eno3) + else + print *, "? ENO3求解器状态: ", state_eno3 + end if + print *, "" + + ! ========== WENO3 求解器 ========== + print *, "[步骤3] 配置并运行WENO3求解器..." + print *, "---------------------------------" + + ! 配置WENO3 + config_weno3%verbose = .true. + config_weno3%ic_type = "step" + config_weno3%wave_speed = 1.0_wp + config_weno3%final_time = 0.625_wp + config_weno3%dt = 0.0025_wp + config_weno3%rk_order = 2 + config_weno3%boundary_type = "periodic" + config_weno3%equation_type = "linear_advection" + config_weno3%problem_type = "linear_advection" + config_weno3%enable_physics = .true. + config_weno3%domain_length = 2.0_wp + + call config_with_reconstruction(config_weno3, "weno3", 3) + + print *, "WENO3配置信息:" + call config_print(config_weno3) + print *, "" + + ! 创建并运行WENO3求解器 + print *, "创建WENO3求解器实例..." + solver_weno3 = physics_solver(config_weno3, mesh) + + print *, "初始化WENO3求解器..." + call solver_weno3%initialize() + + print *, "运行WENO3求解器..." + call solver_weno3%run_to_time(config_weno3%final_time) + + ! 立即保存WENO3结果 + state_weno3 = solver_weno3%get_state() + time_weno3 = solver_weno3%current_time + steps_weno3 = solver_weno3%current_step + error_weno3 = solver_weno3%get_error() + + print *, "检查WENO3求解器状态..." + if (state_weno3 == SOLVER_COMPLETED) then + print *, "✓ WENO3求解器运行完成" + print *, " 最终时间: ", time_weno3 + print *, " 总步数: ", steps_weno3 + else if (state_weno3 == SOLVER_ERROR) then + print *, "✗ WENO3求解器运行失败" + print *, " 错误信息: ", trim(error_weno3) + else + print *, "? WENO3求解器状态: ", state_weno3 + end if + print *, "" + + ! ========== WENO5 求解器 ========== + print *, "[步骤4] 配置并运行WENO5求解器..." + print *, "---------------------------------" + + ! 配置WENO5 + config_weno5%verbose = .true. + config_weno5%ic_type = "step" + config_weno5%wave_speed = 1.0_wp + config_weno5%final_time = 0.625_wp + config_weno5%dt = 0.0025_wp + config_weno5%rk_order = 2 + config_weno5%boundary_type = "periodic" + config_weno5%equation_type = "linear_advection" + config_weno5%problem_type = "linear_advection" + config_weno5%enable_physics = .true. + config_weno5%domain_length = 2.0_wp + + call config_with_reconstruction(config_weno5, "weno", 5) + + print *, "WENO5配置信息:" + call config_print(config_weno5) + print *, "" + + ! 创建并运行WENO5求解器 + print *, "创建WENO5求解器实例..." + solver_weno5 = physics_solver(config_weno5, mesh) + + print *, "初始化WENO5求解器..." + call solver_weno5%initialize() + + print *, "运行WENO5求解器..." + call solver_weno5%run_to_time(config_weno5%final_time) + + ! 立即保存WENO5结果 + state_weno5 = solver_weno5%get_state() + time_weno5 = solver_weno5%current_time + steps_weno5 = solver_weno5%current_step + error_weno5 = solver_weno5%get_error() + + print *, "检查WENO5求解器状态..." + if (state_weno5 == SOLVER_COMPLETED) then + print *, "✓ WENO5求解器运行完成" + print *, " 最终时间: ", time_weno5 + print *, " 总步数: ", steps_weno5 + else if (state_weno5 == SOLVER_ERROR) then + print *, "✗ WENO5求解器运行失败" + print *, " 错误信息: ", trim(error_weno5) + else + print *, "? WENO5求解器状态: ", state_weno5 + end if + print *, "" + + ! ========== 清理求解器 ========== + print *, "[步骤5] 清理求解器..." + print *, "----------------------" + + call solver_eno3%cleanup() + call solver_weno3%cleanup() + call solver_weno5%cleanup() + + ! ========== 清理注册系统 ========== + print *, "[步骤6] 清理注册系统..." + print *, "----------------------" + call registry_cleanup() + + ! ========== 结果汇总 ========== + print *, "" + print *, "==========================================" + print *, " 结果汇总" + print *, "==========================================" + print *, "" + + print *, "求解器状态对比:" + print *, "---------------" + + ! ENO3状态 + if (state_eno3 == SOLVER_COMPLETED) then + status_str = "完成 ✓" + else if (state_eno3 == SOLVER_INITIALIZED) then + status_str = "已初始化" + else if (state_eno3 == SOLVER_ERROR) then + status_str = "错误 ✗" + else + write(status_str, '(A, I3)') "未知状态 ", state_eno3 + end if + print *, "ENO3: ", trim(status_str) + print *, " 最终时间: ", time_eno3 + print *, " 总步数: ", steps_eno3 + if (len_trim(error_eno3) > 0) then + print *, " 错误信息: ", trim(error_eno3) + end if + print *, "" + + ! WENO3状态 + if (state_weno3 == SOLVER_COMPLETED) then + status_str = "完成 ✓" + else if (state_weno3 == SOLVER_INITIALIZED) then + status_str = "已初始化" + else if (state_weno3 == SOLVER_ERROR) then + status_str = "错误 ✗" + else + write(status_str, '(A, I3)') "未知状态 ", state_weno3 + end if + print *, "WENO3: ", trim(status_str) + print *, " 最终时间: ", time_weno3 + print *, " 总步数: ", steps_weno3 + if (len_trim(error_weno3) > 0) then + print *, " 错误信息: ", trim(error_weno3) + end if + print *, "" + + ! WENO5状态 + if (state_weno5 == SOLVER_COMPLETED) then + status_str = "完成 ✓" + else if (state_weno5 == SOLVER_INITIALIZED) then + status_str = "已初始化" + else if (state_weno5 == SOLVER_ERROR) then + status_str = "错误 ✗" + else + write(status_str, '(A, I3)') "未知状态 ", state_weno5 + end if + print *, "WENO5: ", trim(status_str) + print *, " 最终时间: ", time_weno5 + print *, " 总步数: ", steps_weno5 + if (len_trim(error_weno5) > 0) then + print *, " 错误信息: ", trim(error_weno5) + end if + print *, "" + + ! ========== 最终判断 ========== + all_success = (state_eno3 == SOLVER_COMPLETED) .and. & + (state_weno3 == SOLVER_COMPLETED) .and. & + (state_weno5 == SOLVER_COMPLETED) + + if (all_success) then + print *, "✓ 所有求解器成功运行!" + print *, "" + print *, "计算参数总结:" + print *, "--------------" + print *, "网格单元数: ", mesh%ncells + print *, "时间步长: ", config_eno3%dt + print *, "最终时间: ", config_eno3%final_time + print *, "波速: ", config_eno3%wave_speed + print *, "边界条件: ", trim(config_eno3%boundary_type) + print *, "初始条件: ", trim(config_eno3%ic_type) + print *, "" + print *, "重构格式对比:" + print *, "--------------" + print *, "ENO3: 3阶本质无振荡" + print *, "WENO3: 3阶加权本质无振荡" + print *, "WENO5: 5阶加权本质无振荡" + print *, "" + print *, "性能对比:" + print *, "----------" + print *, "ENO3: ", steps_eno3, " 步,最终时间 ", time_eno3 + print *, "WENO3: ", steps_weno3, " 步,最终时间 ", time_weno3 + print *, "WENO5: ", steps_weno5, " 步,最终时间 ", time_weno5 + print *, "" + print *, "下一步开发计划:" + print *, "1. 实现真实的ENO/WENO重构算法" + print *, "2. 实现Rusanov通量计算" + print *, "3. 添加RK时间积分器" + print *, "4. 实现结果输出和可视化" + else + print *, "✗ 有求解器运行失败" + print *, "" + print *, "故障排除:" + print *, "----------" + + if (state_eno3 /= SOLVER_COMPLETED) then + print *, "• ENO3失败: 状态=", state_eno3 + if (len_trim(error_eno3) > 0) then + print *, " 错误信息: ", trim(error_eno3) + end if + end if + + if (state_weno3 /= SOLVER_COMPLETED) then + print *, "• WENO3失败: 状态=", state_weno3 + if (len_trim(error_weno3) > 0) then + print *, " 错误信息: ", trim(error_weno3) + end if + end if + + if (state_weno5 /= SOLVER_COMPLETED) then + print *, "• WENO5失败: 状态=", state_weno5 + if (len_trim(error_weno5) > 0) then + print *, " 错误信息: ", trim(error_weno5) + end if + end if + + print *, "" + print *, "可能的原因:" + print *, "1. 配置参数不正确" + print *, "2. 内存分配失败" + print *, "3. 数值计算不稳定" + end if + + print *, "" + print *, "==========================================" + print *, " 分析完成" + print *, "==========================================" + +end program run_eno_weno \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/build.bat new file mode 100644 index 00000000..6fd6dc03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/build.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Project Builder +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python build script with full Intel environment support... +echo. + +python build.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Build failed + pause + exit /b 1 +) + +echo. +echo [INFO] Build completed successfully! +echo. +echo [INFO] To run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/build.py new file mode 100644 index 00000000..3bf6d537 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/build.py @@ -0,0 +1,629 @@ +#!/usr/bin/env python3 +""" +Fortran CFD Project Builder - 完整Python解决方案 +在Python内部处理Intel oneAPI环境配置 +""" + +import os +import sys +import subprocess +import shutil +import argparse +import time +import platform +import tempfile +from pathlib import Path + +class IntelEnvironment: + """Intel oneAPI环境管理器""" + + def __init__(self): + self.setvars_path = None + self.env_vars = {} + + def find_setvars(self): + """查找setvars.bat文件""" + possible_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + r"C:\Program Files (x86)\Intel\oneAPI\compiler\latest\env\vars.bat", + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\setvars.bat"), + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\compiler\latest\env\vars.bat"), + ] + + for path in possible_paths: + if os.path.exists(path): + self.setvars_path = path + return True + + return False + + def setup_environment(self): + """设置Intel环境""" + if not self.find_setvars(): + return False + + try: + # 创建临时的批处理文件来捕获环境变量 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + f.write(f'@echo off\n') + f.write(f'call "{self.setvars_path}" >nul 2>&1\n') + f.write(f'set\n') # 输出所有环境变量 + temp_bat = f.name + + # 运行批处理文件并捕获输出 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + shell=True + ) + + # 解析环境变量 + for line in result.stdout.split('\n'): + line = line.strip() + if '=' in line: + key, value = line.split('=', 1) + self.env_vars[key.strip()] = value.strip() + + # 清理临时文件 + os.unlink(temp_bat) + + # 更新当前进程的环境变量 + os.environ.update(self.env_vars) + + return True + + except Exception as e: + print(f"设置Intel环境失败: {e}") + return False + + def get_compiler_info(self): + """获取编译器信息""" + info = {} + + # 检查ifx编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + env={**os.environ, **self.env_vars} if self.env_vars else os.environ + ) + + if result.returncode == 0: + for line in result.stdout.split('\n'): + if 'Version' in line or '版本' in line: + info['ifx_version'] = line.strip() + break + except: + pass + + # 检查环境变量 + info['ifx_root'] = self.env_vars.get('IFX_ROOT', '') + info['compiler_root'] = self.env_vars.get('ONEAPI_ROOT', '') + + return info + +class BuildSystem: + """构建系统主类""" + + def __init__(self): + self.project_root = Path(__file__).parent.parent + self.build_dir = self.project_root / "build" + self.intel_env = IntelEnvironment() + + # 设置控制台编码 + if sys.platform == "win32": + try: + import ctypes + # 设置控制台输出为UTF-8 + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + except: + pass + + def print_header(self, text): + """打印标题""" + print(f"\n{'='*70}") + print(f" {text}") + print(f"{'='*70}\n") + + def print_step(self, step, total, message): + """打印步骤""" + print(f"[{step}/{total}] {message}...") + + def print_success(self, message): + """打印成功""" + print(f"\033[92m✓ {message}\033[0m") + + def print_error(self, message): + """打印错误""" + print(f"\033[91m✗ {message}\033[0m") + + def print_warning(self, message): + """打印警告""" + print(f"\033[93m! {message}\033[0m") + + def print_info(self, message): + """打印信息""" + print(f"\033[94mℹ {message}\033[0m") + + def check_prerequisites(self): + """检查前提条件""" + self.print_step(1, 6, "检查前提条件") + + # 检查Python版本 + python_version = sys.version.split()[0] + self.print_info(f"Python版本: {python_version}") + + # 检查平台 + self.print_info(f"平台: {platform.system()} {platform.release()}") + self.print_info(f"处理器核心数: {os.cpu_count()}") + + # 检查CMake + try: + result = subprocess.run( + ["cmake", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + version_line = result.stdout.split('\n')[0] + self.print_success(f"CMake: {version_line}") + else: + self.print_error("CMake未找到") + return False + except FileNotFoundError: + self.print_error("CMake未安装") + return False + + return True + + def setup_intel_environment(self, args): + """设置Intel环境""" + self.print_step(2, 6, "配置Intel oneAPI环境") + + if not self.intel_env.find_setvars(): + self.print_warning("未找到Intel oneAPI setvars.bat") + self.print_info("将尝试使用系统环境中的编译器") + + # 检查是否能直接访问编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + self.print_success("Intel编译器在系统PATH中找到") + return True + else: + self.print_warning("Intel编译器未在PATH中找到") + except: + self.print_warning("无法访问Intel编译器") + + return True # 继续,让CMake自己找编译器 + + # 设置环境 + if self.intel_env.setup_environment(): + compiler_info = self.intel_env.get_compiler_info() + + if compiler_info.get('ifx_version'): + self.print_success(f"Intel Fortran编译器: {compiler_info['ifx_version']}") + elif compiler_info.get('ifx_root'): + self.print_success(f"Intel编译器路径: {compiler_info['ifx_root']}") + else: + self.print_success("Intel oneAPI环境配置完成") + + return True + else: + self.print_warning("Intel环境配置失败,将继续使用系统环境") + return True + + def clean_build_directory(self, args): + """清理构建目录""" + if args.clean and self.build_dir.exists(): + self.print_info("清理构建目录...") + try: + shutil.rmtree(self.build_dir) + self.print_success("构建目录已清理") + except Exception as e: + self.print_error(f"清理失败: {e}") + if not args.force: + return False + return True + + def run_command(self, cmd, cwd=None, check=True, env=None): + """运行命令""" + if isinstance(cmd, list): + cmd_str = ' '.join(str(c) for c in cmd if c) + else: + cmd_str = str(cmd) + + print(f" \033[96m$\033[0m {cmd_str}") + + try: + # 合并环境变量 + exec_env = os.environ.copy() + if env: + exec_env.update(env) + if self.intel_env.env_vars: + exec_env.update(self.intel_env.env_vars) + + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=False, + env=exec_env + ) + + # 处理输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or '完成' in line or '生成' in line: + print(f" \033[92m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + if check and result.returncode != 0: + self.print_error(f"命令执行失败,退出码: {result.returncode}") + return False + + return True + + except Exception as e: + self.print_error(f"命令执行异常: {e}") + return False + + def configure_cmake(self, args): + """配置CMake""" + self.print_step(3, 6, "配置CMake项目") + + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ] + + if args.compiler == "ifx": + cmake_cmd.extend(["-T", "fortran=ifx"]) + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + success = self.run_command(cmake_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("CMake配置完成") + else: + self.print_error("CMake配置失败") + + return success + + def build_project(self, args): + """构建项目""" + self.print_step(4, 6, "构建项目") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + success = self.run_command(build_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("项目构建完成") + else: + self.print_error("构建失败") + + return success + + def run_tests_with_environment(self, test_exe): + """运行单个测试,确保有Intel环境""" + try: + # 创建临时的批处理文件来运行测试 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'"{test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'"{test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + return result + + except Exception as e: + print(f"运行测试失败: {e}") + return None + + def run_tests(self, args): + """运行测试""" + self.print_step(5, 6, "运行测试") + + # 查找测试可执行文件 + test_dir = self.build_dir / "bin" / args.build_type + if not test_dir.exists(): + test_dir = self.build_dir / "bin" + if not test_dir.exists(): + test_dir = self.build_dir + + test_files = list(test_dir.glob("test_*.exe")) + + if not test_files: + self.print_warning("未找到测试程序") + return True + + all_passed = True + + for test_exe in sorted(test_files): + test_name = test_exe.stem + self.print_info(f"运行测试: {test_name}") + print(f" {'-'*50}") + + # 运行测试 + result = self.run_tests_with_environment(str(test_exe)) + + if result is None: + self.print_error(f" {test_name} 运行失败") + all_passed = False + continue + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + print(f" {line}") + + if result.returncode == 0: + self.print_success(f" {test_name} 通过") + else: + self.print_error(f" {test_name} 失败 (退出码: {result.returncode})") + all_passed = False + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + print() # 空行 + + return all_passed + + def create_test_runner(self, args): + """创建独立的测试运行器""" + self.print_step(6, 6, "创建测试运行器") + + runner_path = self.build_dir / "run_tests.bat" + + content = f'''@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Test Runner +echo ======================================== +echo. + +REM Setup Intel oneAPI environment +set "SETVARS_PATH={self.intel_env.setvars_path or ''}" +if exist "%SETVARS_PATH%" ( + call "%SETVARS_PATH%" >nul + echo [INFO] Intel environment configured +) else ( + echo [WARNING] Intel environment not found + echo [WARNING] Tests may fail without runtime libraries +) + +echo. + +REM Run all test executables +set "TEST_COUNT=0" +set "PASS_COUNT=0" + +for %%f in ("bin\\{args.build_type}\\test_*.exe") do ( + set /a TEST_COUNT+=1 + echo [TEST %%f] + echo {'-'*50} + + %%f + if errorlevel 1 ( + echo [FAILED] %%f + ) else ( + echo [PASSED] %%f + set /a PASS_COUNT+=1 + ) + echo. +) + +echo ======================================== +echo Tests: %PASS_COUNT%/%TEST_COUNT% passed +if %PASS_COUNT% equ %TEST_COUNT% ( + echo [SUCCESS] All tests passed! +) else ( + echo [FAILURE] Some tests failed +) +echo ======================================== + +pause +''' + + with open(runner_path, 'w', encoding='utf-8') as f: + f.write(content) + + self.print_success(f"测试运行器已创建: {runner_path}") + self.print_info(f"使用方法: cd build && run_tests.bat") + + return runner_path + + def generate_report(self, args, build_time, tests_passed): + """生成构建报告""" + self.print_header("构建完成") + + print(f"项目: {self.project_root.name}") + print(f"构建类型: {args.build_type}") + print(f"编译器: {args.compiler}") + print(f"并行作业: {args.jobs}") + print(f"总耗时: {build_time:.1f}秒") + print(f"测试结果: {'全部通过' if tests_passed else '有失败'}") + + # 显示生成的可执行文件 + bin_dir = self.build_dir / "bin" / args.build_type + if bin_dir.exists(): + print(f"\n生成的可执行文件:") + for exe in sorted(bin_dir.glob("*.exe")): + size_mb = exe.stat().st_size / (1024 * 1024) + print(f" • {exe.name} ({size_mb:.2f} MB)") + + # 显示测试运行器信息 + runner_path = self.build_dir / "run_tests.bat" + if runner_path.exists(): + print(f"\n独立测试运行器:") + print(f" • {runner_path.name}") + print(f" 在Intel oneAPI环境中运行所有测试") + + def run(self): + """运行构建系统""" + parser = argparse.ArgumentParser( + description="Fortran CFD项目构建工具", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认构建 + %(prog)s --clean # 清理后构建 + %(prog)s --build-type Release # Release构建 + %(prog)s --no-tests # 只构建,不运行测试 + %(prog)s -j8 --verbose # 8线程并行构建,详细输出 + """ + ) + + parser.add_argument("--build-type", choices=["Debug", "Release"], + default="Debug", help="构建类型") + parser.add_argument("--compiler", choices=["ifx", "ifort"], + default="ifx", help="Fortran编译器") + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-tests", action="store_true", + help="跳过测试") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + + args = parser.parse_args() + + # 开始构建 + start_time = time.time() + + self.print_header("Fortran CFD 项目构建系统") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 检查前提条件 + if not self.check_prerequisites(): + if not args.force: + return 1 + + # 2. 设置Intel环境 + if not self.setup_intel_environment(args): + if not args.force: + return 1 + + # 3. 清理目录 + if not self.clean_build_directory(args): + if not args.force: + return 1 + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 4. 配置CMake + if not self.configure_cmake(args): + if not args.force: + return 1 + + # 5. 构建项目 + if not self.build_project(args): + if not args.force: + return 1 + + # 6. 运行测试和创建测试运行器 + tests_passed = True + if not args.no_tests: + tests_passed = self.run_tests(args) + + # 创建测试运行器 + self.create_test_runner(args) # 传递 args 参数 + + # 7. 生成报告 + build_time = time.time() - start_time + self.generate_report(args, build_time, tests_passed) + + return 0 if tests_passed else 1 + + except KeyboardInterrupt: + self.print_error("\n构建被用户中断") + return 1 + except Exception as e: + self.print_error(f"构建过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + builder = BuildSystem() + return builder.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/run_all_steps.bat b/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/run_all_steps.bat new file mode 100644 index 00000000..d506149b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/run_all_steps.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo CFD Project: All Steps +echo ======================================== +echo. + +echo [INFO] Starting Step 1: Physics Modules Test... +call run_step1.bat + +if errorlevel 1 ( + echo [ERROR] Step 1 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Starting Step 2: Configuration Physics Update... +call run_step2.bat + +if errorlevel 1 ( + echo [ERROR] Step 2 failed + pause + exit /b 1 +) + +echo. +echo ======================================== +echo All Steps Completed Successfully! +echo ======================================== +echo. +echo [INFO] Next: Update component manager for physics support +echo [INFO] Run: run_step3.bat (to be created) +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/run_example.py b/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/run_example.py new file mode 100644 index 00000000..d7c19917 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/run_example.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +# scripts/run_example.py +""" +运行ENO/WENO示例程序 +""" + +import os +import sys +import subprocess +from pathlib import Path + +# 添加当前目录到路径 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import BuildSystem +except ImportError: + print("错误: 找不到build.py,请确保在scripts目录中运行") + sys.exit(1) + +def run_example(): + """运行示例程序""" + builder = BuildSystem() + + print("\n" + "="*70) + print(" 运行ENO/WENO对比示例程序") + print("="*70 + "\n") + + # 检查是否已构建 + exe_path = builder.build_dir / "bin" / "Debug" / "example_eno_weno_comparison.exe" + + if not exe_path.exists(): + print("示例程序未构建,先构建项目...") + print("-"*50) + + # 使用简化的构建 + result = subprocess.run( + ["python", "build.py", "--no-tests", "--clean"], + cwd=builder.project_root / "scripts", + capture_output=True, + text=True + ) + + if result.returncode != 0: + print("构建失败:") + print(result.stderr) + return False + + # 运行示例程序 + print("运行示例程序...") + print("-"*50) + + try: + result = subprocess.run( + [str(exe_path)], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace' + ) + + print(result.stdout) + + if result.stderr: + print("标准错误输出:") + print(result.stderr) + + return result.returncode == 0 + + except Exception as e: + print(f"运行示例程序失败: {e}") + return False + +def main(): + """主函数""" + success = run_example() + + if success: + print("\n" + "="*70) + print(" ✓ 示例程序运行成功") + print("="*70) + return 0 + else: + print("\n" + "="*70) + print(" ✗ 示例程序运行失败") + print("="*70) + return 1 + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/run_step1.bat b/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/run_step1.bat new file mode 100644 index 00000000..0b6b1f17 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/run_step1.bat @@ -0,0 +1,39 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Step 1: Physics Modules Test +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python step1 script with full Intel environment support... +echo. + +python run_step1.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Step 1 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Step 1 completed successfully! +echo. +echo [INFO] Next step: Update config to include physics settings +echo [INFO] Run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/run_step1.py b/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/run_step1.py new file mode 100644 index 00000000..5e087a69 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/run_step1.py @@ -0,0 +1,319 @@ +#!/usr/bin/env python3 +""" +Step 1: Physics Modules Test +扩展build.py,专门用于测试物理模块 +""" + +import os +import sys +import subprocess +import time +from pathlib import Path + +# 添加当前目录到路径,以便导入build.py的类 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import IntelEnvironment, BuildSystem +except ImportError: + print("Error: Cannot import build.py. Make sure build.py is in the same directory.") + sys.exit(1) + +class Step1System(BuildSystem): + """Step 1 测试系统,继承自BuildSystem""" + + def __init__(self): + super().__init__() + self.test_name = "test_physics_minimal" + self.test_exe = None + + def find_test_executable(self): + """查找测试可执行文件""" + possible_paths = [ + self.build_dir / "bin" / "Debug" / f"{self.test_name}.exe", + self.build_dir / "Debug" / f"{self.test_name}.exe", + self.build_dir / f"{self.test_name}.exe", + self.build_dir / "bin" / f"{self.test_name}.exe", + ] + + for path in possible_paths: + if path.exists(): + self.test_exe = path + self.print_success(f"Found test executable: {path}") + return True + + # 如果没有找到,尝试搜索 + self.print_warning(f"Could not find {self.test_name}.exe") + self.print_info("Searching for test executables...") + + try: + result = subprocess.run( + ["dir", str(self.build_dir), "/s", "/b", "*.exe"], + capture_output=True, + text=True, + encoding='utf-8', + shell=True + ) + + if result.returncode == 0: + test_files = [line.strip() for line in result.stdout.split('\n') + if line and 'test_' in line.lower()] + + if test_files: + self.print_info("Found test files:") + for test_file in test_files: + self.print_info(f" {test_file}") + return False + except: + pass + + return False + + def run_test_with_intel_env(self): + """在Intel环境下运行测试""" + if not self.test_exe: + self.print_error("No test executable found") + return False + + self.print_step(1, 2, f"Running test: {self.test_exe.name}") + + try: + # 创建临时的批处理文件来运行测试(包含Intel环境) + import tempfile + + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + # 设置Intel环境 + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'echo [INFO] Intel environment configured\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'echo [WARNING] Intel environment not found\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + self.print_info(f"Command: {temp_bat}") + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower() or 'fail' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or 'pass' in line.lower() or '✓' in line: + print(f" \033[92m{line}\033[0m") + elif '=' in line or '---' in line: + print(f" \033[96m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + self.print_warning("Test stderr output:") + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + self.print_step(2, 2, "Test execution completed") + + if result.returncode == 0: + self.print_success("Test passed") + return True + else: + self.print_error(f"Test failed (exit code: {result.returncode})") + return False + + except Exception as e: + self.print_error(f"Failed to run test: {e}") + return False + + def build_project_if_needed(self, args): + """如果需要,构建项目""" + if args.no_build: + self.print_info("Skipping build (--no-build flag)") + return True + + self.print_step(1, 3, "Building project") + + # 调用父类的构建方法 + build_args = argparse.Namespace() + build_args.clean = args.clean + build_args.build_type = "Debug" + build_args.compiler = "ifx" + build_args.no_tests = True # 不运行所有测试 + build_args.jobs = os.cpu_count() + build_args.verbose = args.verbose + build_args.force = args.force + + # 清理构建目录 + if args.clean and self.build_dir.exists(): + self.print_info("Cleaning build directory...") + import shutil + try: + shutil.rmtree(self.build_dir) + self.print_success("Build directory cleaned") + except Exception as e: + self.print_error(f"Clean failed: {e}") + if not args.force: + return False + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 配置CMake + self.print_step(2, 3, "Configuring CMake") + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-DCMAKE_BUILD_TYPE=Debug", + "-T", "fortran=ifx", + ] + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + if not self.run_command(cmake_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + # 构建项目 + self.print_step(3, 3, "Building project") + build_cmd = [ + "cmake", + "--build", ".", + "--config", "Debug", + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + if not self.run_command(build_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + self.print_success("Build completed") + return True + + def run(self): + """运行Step 1测试""" + parser = argparse.ArgumentParser( + description="Step 1: Physics Modules Test", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认运行 + %(prog)s --clean # 清理后构建并测试 + %(prog)s --no-build # 只运行测试,不重新构建 + %(prog)s --verbose # 详细输出 + %(prog)s -j4 # 使用4个并行作业构建 + """ + ) + + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-build", action="store_true", + help="不重新构建,直接运行测试") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + + args = parser.parse_args() + + # 开始测试 + start_time = time.time() + + self.print_header("Step 1: Physics Modules Implementation Test") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 设置Intel环境 + self.print_step(1, 4, "Setting up Intel oneAPI environment") + if not self.setup_intel_environment(args): + if not args.force: + self.print_error("Intel environment setup failed") + return 1 + self.print_warning("Intel environment setup failed, continuing...") + + # 2. 构建项目(如果需要) + self.print_step(2, 4, "Building project if needed") + if not self.build_project_if_needed(args): + if not args.force: + return 1 + + # 3. 查找测试可执行文件 + self.print_step(3, 4, "Finding test executable") + if not self.find_test_executable(): + if not args.force: + return 1 + self.print_warning("Test executable not found, but continuing due to --force") + return 0 + + # 4. 运行测试 + self.print_step(4, 4, "Running physics module test") + test_passed = self.run_test_with_intel_env() + + # 生成报告 + test_time = time.time() - start_time + self.print_header("Step 1 Complete") + + print(f"测试: {'通过 ✓' if test_passed else '失败 ✗'}") + print(f"测试程序: {self.test_exe.name if self.test_exe else '未找到'}") + print(f"总耗时: {test_time:.1f}秒") + + if test_passed: + print(f"\n下一步: 更新配置以包含物理设置") + print(f"建议: 修改config.f90,添加physics相关字段") + return 0 + else: + if args.force: + self.print_warning("测试失败,但由于--force标志继续执行") + return 0 + return 1 + + except KeyboardInterrupt: + self.print_error("\n测试被用户中断") + return 1 + except Exception as e: + self.print_error(f"测试过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + system = Step1System() + return system.run() + +if __name__ == "__main__": + # 需要导入argparse + import argparse + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/run_step2.bat b/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/run_step2.bat new file mode 100644 index 00000000..9c1f62de --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/run_step2.bat @@ -0,0 +1,39 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Step 2: Configuration Physics Update +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python step2 script with full Intel environment support... +echo. + +python run_step2.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Step 2 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Step 2 completed successfully! +echo. +echo [INFO] Next step: Update component manager to support physics +echo [INFO] Run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/run_step2.py b/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/run_step2.py new file mode 100644 index 00000000..c16b7608 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/scripts/run_step2.py @@ -0,0 +1,284 @@ +#!/usr/bin/env python3 +""" +Step 2: Configuration Physics Update +测试配置模块的物理功能更新 +""" + +import os +import sys +import subprocess +import time +from pathlib import Path + +# 添加当前目录到路径,以便导入build.py的类 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import IntelEnvironment, BuildSystem +except ImportError: + print("Error: Cannot import build.py. Make sure build.py is in the same directory.") + sys.exit(1) + +class Step2System(BuildSystem): + """Step 2 测试系统,继承自BuildSystem""" + + def __init__(self): + super().__init__() + self.test_name = "test_config_physics" + self.test_exe = None + + def find_test_executable(self): + """查找测试可执行文件""" + possible_paths = [ + self.build_dir / "bin" / "Debug" / f"{self.test_name}.exe", + self.build_dir / "Debug" / f"{self.test_name}.exe", + self.build_dir / f"{self.test_name}.exe", + self.build_dir / "bin" / f"{self.test_name}.exe", + ] + + for path in possible_paths: + if path.exists(): + self.test_exe = path + self.print_success(f"Found test executable: {path}") + return True + + return False + + def run_test_with_intel_env(self): + """在Intel环境下运行测试""" + if not self.test_exe: + self.print_error("No test executable found") + return False + + self.print_step(1, 2, f"Running test: {self.test_exe.name}") + + try: + # 创建临时的批处理文件来运行测试(包含Intel环境) + import tempfile + + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + # 设置Intel环境 + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'echo [INFO] Intel environment configured\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'echo [WARNING] Intel environment not found\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + self.print_info(f"Command: {temp_bat}") + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower() or 'fail' in line.lower() or '✗' in line: + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or 'pass' in line.lower() or '✓' in line: + print(f" \033[92m{line}\033[0m") + elif '=' in line or '---' in line or '===' in line: + print(f" \033[96m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + self.print_warning("Test stderr output:") + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + self.print_step(2, 2, "Test execution completed") + + if result.returncode == 0: + self.print_success("Test passed") + return True + else: + self.print_error(f"Test failed (exit code: {result.returncode})") + return False + + except Exception as e: + self.print_error(f"Failed to run test: {e}") + return False + + def build_project(self, args): + """构建项目""" + if args.no_build: + self.print_info("Skipping build (--no-build flag)") + return True + + self.print_step(1, 3, "Building project") + + # 清理构建目录 + if args.clean and self.build_dir.exists(): + self.print_info("Cleaning build directory...") + import shutil + try: + shutil.rmtree(self.build_dir) + self.print_success("Build directory cleaned") + except Exception as e: + self.print_error(f"Clean failed: {e}") + if not args.force: + return False + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 配置CMake + self.print_step(2, 3, "Configuring CMake") + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-DCMAKE_BUILD_TYPE=Debug", + "-T", "fortran=ifx", + ] + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + if not self.run_command(cmake_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + # 构建项目 + self.print_step(3, 3, "Building project") + build_cmd = [ + "cmake", + "--build", ".", + "--config", "Debug", + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + if not self.run_command(build_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + self.print_success("Build completed") + return True + + def run(self): + """运行Step 2测试""" + import argparse + + parser = argparse.ArgumentParser( + description="Step 2: Configuration Physics Update", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认运行 + %(prog)s --clean # 清理后构建并测试 + %(prog)s --no-build # 只运行测试,不重新构建 + %(prog)s --verbose # 详细输出 + """ + ) + + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-build", action="store_true", + help="不重新构建,直接运行测试") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + + args = parser.parse_args() + + # 开始测试 + start_time = time.time() + + self.print_header("Step 2: Configuration Physics Update") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 设置Intel环境 + self.print_step(1, 4, "Setting up Intel oneAPI environment") + if not self.setup_intel_environment(args): + if not args.force: + self.print_error("Intel environment setup failed") + return 1 + self.print_warning("Intel environment setup failed, continuing...") + + # 2. 构建项目(如果需要) + self.print_step(2, 4, "Building project if needed") + if not self.build_project(args): + if not args.force: + return 1 + + # 3. 查找测试可执行文件 + self.print_step(3, 4, "Finding test executable") + if not self.find_test_executable(): + self.print_error(f"Test executable {self.test_name}.exe not found") + if not args.force: + return 1 + self.print_warning("Test executable not found, but continuing due to --force") + return 0 + + # 4. 运行测试 + self.print_step(4, 4, "Running configuration physics test") + test_passed = self.run_test_with_intel_env() + + # 生成报告 + test_time = time.time() - start_time + self.print_header("Step 2 Complete") + + print(f"测试: {'通过 ✓' if test_passed else '失败 ✗'}") + print(f"测试程序: {self.test_exe.name if self.test_exe else '未找到'}") + print(f"总耗时: {test_time:.1f}秒") + + if test_passed: + print(f"\n下一步: 更新组件管理器以支持物理模块") + print(f"建议: 修改component_manager.f90,添加physics组件创建") + return 0 + else: + if args.force: + self.print_warning("测试失败,但由于--flag继续执行") + return 0 + return 1 + + except KeyboardInterrupt: + self.print_error("\n测试被用户中断") + return 1 + except Exception as e: + self.print_error(f"测试过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + system = Step2System() + return system.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03c/src/CMakeLists.txt new file mode 100644 index 00000000..59ad3c3f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/CMakeLists.txt @@ -0,0 +1,31 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(base) +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) +add_subdirectory(physics) # ← 新增物理模块目录 +add_subdirectory(manager) +add_subdirectory(solver) + +message(STATUS "源代码目录配置完成") + +add_executable(run_eno_weno + ${CMAKE_CURRENT_SOURCE_DIR}/run_eno_weno.f90 +) + +target_link_libraries(run_eno_weno + PRIVATE + solver + infrastructure + core + physics + manager +) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/base/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03c/src/base/CMakeLists.txt new file mode 100644 index 00000000..74f4aa65 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/base/CMakeLists.txt @@ -0,0 +1,16 @@ +# src/base/CMakeLists.txt +message(STATUS "Configuring base module...") + +add_library(base STATIC + modules.f90 + precision.f90 # 新增 +) + +set_target_properties(base PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Base module configured") + +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/base/modules.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/src/base/modules.f90 new file mode 100644 index 00000000..43aaee24 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/base/modules.f90 @@ -0,0 +1,36 @@ +! src/base/modules.f90 +module base_modules + use, intrinsic :: iso_fortran_env, only: real64, int32 + implicit none + + public :: wp, ip, max_name_len, string_len, cfd_config_base, component_info + + integer, parameter :: wp = real64 + integer, parameter :: ip = int32 + integer, parameter :: string_len = 100 + integer, parameter :: max_name_len = 32 + + ! 基础配置类型 + type :: cfd_config_base + character(len=max_name_len) :: ic_type = "step" + character(len=max_name_len) :: recon_scheme = "eno" + character(len=max_name_len) :: flux_type = "rusanov" + integer(ip) :: rk_order = 1 + real(wp) :: wave_speed = 1.0_wp + real(wp) :: final_time = 0.625_wp + real(wp) :: dt = 0.025_wp + character(len=max_name_len) :: boundary_type = "periodic" + integer(ip) :: spatial_order = 2 + character(len=max_name_len) :: equation_type = "linear_advection" + character(len=max_name_len) :: problem_type = "linear_advection" + logical :: verbose = .true. + end type cfd_config_base + + ! 组件信息类型 + type :: component_info + character(len=max_name_len) :: category = "" + character(len=max_name_len) :: name = "" + integer(ip) :: order = 0 + end type component_info + +end module base_modules \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/base/precision.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/src/base/precision.f90 new file mode 100644 index 00000000..4ac5fd7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/base/precision.f90 @@ -0,0 +1,9 @@ +! src/base/precision.f90(简单版本) +module precision_module + use base_modules, only: wp, ip + implicit none + + ! 重新导出,确保兼容 + public :: wp, ip + +end module precision_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03c/src/core/CMakeLists.txt new file mode 100644 index 00000000..d8b8df06 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/core/CMakeLists.txt @@ -0,0 +1,14 @@ +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") + +add_library(core STATIC + registry.f90 +) + +target_link_libraries(core PRIVATE base) + +set_target_properties(core PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Core module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/core/factory_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/src/core/factory_base.f90 new file mode 100644 index 00000000..302418a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/core/factory_base.f90 @@ -0,0 +1,57 @@ +! src/core/factory_base.f90 +module factory_base_module + use base_modules, only: wp, ip + use registry_module, only: create_component, has_component + + implicit none + private + public :: wp, ip, factory_base, factory_create + + ! 工厂基类 + type :: factory_base + character(len=max_name_length) :: category = "" + contains + procedure :: create => factory_base_create + procedure :: get_available => factory_base_get_available + end type factory_base + + ! 便捷函数类型 + abstract interface + function factory_function_interface(category, name) result(instance) + import :: wp + character(len=*), intent(in) :: category, name + class(*), allocatable :: instance + end function factory_function_interface + end interface + +contains + + ! 创建工厂实例 + function factory_create(category) result(factory) + character(len=*), intent(in) :: category + type(factory_base) :: factory + factory%category = trim(category) + end function factory_create + + ! 工厂创建方法 + function factory_base_create(this, name) result(instance) + class(factory_base), intent(in) :: this + character(len=*), intent(in) :: name + class(*), allocatable :: instance + + instance = create_component(this%category, name) + end function factory_base_create + + ! 获取可用组件列表(简化版) + subroutine factory_base_get_available(this, names, count) + class(factory_base), intent(in) :: this + character(len=*), allocatable, intent(out) :: names(:) + integer(ip), intent(out) :: count + + ! 这里需要实现从注册表获取列表的逻辑 + ! 暂时返回空列表 + count = 0 + allocate(character(len=max_name_length) :: names(0)) + end subroutine factory_base_get_available + +end module factory_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..a960167c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/src/core/registry.f90 new file mode 100644 index 00000000..d155aa19 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/core/registry.f90 @@ -0,0 +1,257 @@ +! src/core/registry.f90 (更新版) +module registry_module + use base_modules, only: wp, ip, max_name_len, component_info + + implicit none + private + + ! 明确公开所有需要的接口 + public :: wp, ip ! 类型参数 + public :: component_info ! 类型 + public :: registry_init, registry_cleanup ! 初始化/清理 + public :: register_component_simple ! 注册组件 + public :: has_component_simple ! 检查组件 + public :: list_components ! 列出组件 + public :: registry_is_initialized ! 检查初始化状态 + public :: registry_get_size ! 获取大小 + public :: initialize_default_components ! 新增:初始化默认组件 + + ! 全局注册表 + type :: component_registry + type(component_info), allocatable :: components(:) + integer(ip) :: count = 0 + integer(ip) :: capacity = 100 + logical :: initialized = .false. + logical :: verbose = .true. + logical :: default_components_added = .false. ! 新增:标记是否已添加默认组件 + end type component_registry + + type(component_registry) :: registry + +contains + + ! ==================== 公共API ==================== + + subroutine registry_init(verbose) + logical, optional, intent(in) :: verbose + + if (registry%initialized) then + if (registry%verbose) then + print *, "[REGISTRY] Already initialized" + end if + return + end if + + if (present(verbose)) then + registry%verbose = verbose + end if + + allocate(registry%components(registry%capacity)) + registry%initialized = .true. + + if (registry%verbose) then + print *, "[REGISTRY] Initialized with capacity:", registry%capacity + end if + end subroutine registry_init + + subroutine registry_cleanup() + if (allocated(registry%components)) then + deallocate(registry%components) + end if + registry%initialized = .false. + registry%count = 0 + registry%default_components_added = .false. ! 重置标记 + + if (registry%verbose) then + print *, "[REGISTRY] Cleaned up" + end if + end subroutine registry_cleanup + + ! 新增:初始化默认组件 + subroutine initialize_default_components() + if (.not. registry%initialized) then + call registry_init() + end if + + if (registry%default_components_added) then + if (registry%verbose) then + print *, "[REGISTRY] Default components already added" + end if + return + end if + + ! 注册重构器 + call register_component_simple("reconstructor", "eno", order=3) + call register_component_simple("reconstructor", "weno3", order=3) + call register_component_simple("reconstructor", "weno5", order=5) + + ! 注册通量计算器 + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + + ! 注册边界条件 + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + + ! 注册时间积分器 + call register_component_simple("integrator", "rk1", order=1) + call register_component_simple("integrator", "rk2", order=2) + call register_component_simple("integrator", "rk3", order=3) + + ! 注册方程 + call register_component_simple("equation", "linear_advection") + + ! 注册问题 + call register_component_simple("problem", "linear_advection") + + ! 注册初始条件 + call register_component_simple("initial_condition", "step") + call register_component_simple("initial_condition", "sin") + call register_component_simple("initial_condition", "gaussian") + + registry%default_components_added = .true. + + if (registry%verbose) then + print *, "[REGISTRY] Default components registered" + print *, "[REGISTRY] Total components:", registry%count + end if + end subroutine initialize_default_components + + subroutine register_component_simple(category, name, order) + character(len=*), intent(in) :: category, name + integer(ip), optional, intent(in) :: order + + integer(ip) :: i + type(component_info) :: info + + if (.not. registry%initialized) then + call registry_init() + end if + + ! 检查是否已存在 + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + if (registry%verbose) then + print *, "[WARN] Overwriting component: ", trim(category), ".", trim(name) + end if + + ! 更新 + if (present(order)) then + registry%components(i)%order = order + else + registry%components(i)%order = 0 + end if + return + end if + end do + + ! 扩展数组 + if (registry%count >= registry%capacity) then + call expand_registry() + end if + + ! 添加新组件 + registry%count = registry%count + 1 + + info%category = trim(category) + info%name = trim(name) + info%order = 0 + if (present(order)) then + info%order = order + end if + + registry%components(registry%count) = info + + if (registry%verbose) then + print *, "[OK] Registered simple: ", trim(category), ".", trim(name) + end if + end subroutine register_component_simple + + logical function has_component_simple(category, name) + character(len=*), intent(in) :: category, name + + integer(ip) :: i + + has_component_simple = .false. + + if (.not. registry%initialized) return + + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + has_component_simple = .true. + return + end if + end do + end function has_component_simple + + subroutine list_components(category) + character(len=*), optional, intent(in) :: category + + integer(ip) :: i, count + + if (.not. registry%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + if (registry%count == 0) then + print *, "[INFO] No components registered" + return + end if + + count = 0 + print *, "=== Registry Contents ===" + do i = 1, registry%count + if (.not. present(category) .or. & + trim(registry%components(i)%category) == trim(category)) then + call print_component_info(registry%components(i)) + count = count + 1 + end if + end do + + print *, "Total:", count, "components" + print *, "==========================" + end subroutine list_components + + ! ==================== 新增函数 ==================== + + logical function registry_is_initialized() + ! 检查注册表是否已初始化 + registry_is_initialized = registry%initialized + end function registry_is_initialized + + integer(ip) function registry_get_size() + ! 获取注册表中的组件数量 + registry_get_size = registry%count + end function registry_get_size + + ! ==================== 内部辅助函数 ==================== + + subroutine expand_registry() + type(component_info), allocatable :: temp(:) + + registry%capacity = registry%capacity * 2 + allocate(temp(registry%capacity)) + temp(1:registry%count) = registry%components(1:registry%count) + call move_alloc(temp, registry%components) + + if (registry%verbose) then + print *, "[INFO] Registry expanded to capacity:", registry%capacity + end if + end subroutine expand_registry + + subroutine print_component_info(info) + type(component_info), intent(in) :: info + + if (info%order > 0) then + print *, " [", trim(info%category), ".", trim(info%name), & + " (order:", info%order, ")]" + else + print *, " [", trim(info%category), ".", trim(info%name), "]" + end if + end subroutine print_component_info + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/core/registry_initializer.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/src/core/registry_initializer.f90 new file mode 100644 index 00000000..44023d1d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/core/registry_initializer.f90 @@ -0,0 +1,39 @@ +! src/core/registry_initializer.f90 (新增文件) +module registry_initializer_module + use registry_module, only: register_component_simple + implicit none + private + public :: initialize_default_registry + +contains + + subroutine initialize_default_registry() + ! 注册重构器 + call register_component_simple("reconstructor", "eno", order=3) + call register_component_simple("reconstructor", "weno3", order=3) + call register_component_simple("reconstructor", "weno5", order=5) + + ! 注册通量计算器 + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + + ! 注册边界条件 + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + + ! 注册时间积分器 + call register_component_simple("integrator", "rk1", order=1) + call register_component_simple("integrator", "rk2", order=2) + call register_component_simple("integrator", "rk3", order=3) + + ! 注册方程 + call register_component_simple("equation", "linear_advection") + + ! 注册问题 + call register_component_simple("problem", "linear_advection") + + print *, "[REGISTRY] Default components registered" + end subroutine initialize_default_registry + +end module registry_initializer_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03c/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..70cbbd2f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/infrastructure/CMakeLists.txt @@ -0,0 +1,17 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "Configuring infrastructure module...") + +add_library(infrastructure STATIC + config.f90 + mesh.f90 + domain.f90 # 新增 + solution.f90 # 新增 +) + +target_link_libraries(infrastructure PRIVATE base) + +set_target_properties(infrastructure PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Infrastructure module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/src/infrastructure/config.f90 new file mode 100644 index 00000000..7586a1a5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/infrastructure/config.f90 @@ -0,0 +1,144 @@ +! src/infrastructure/config.f90 (修复版) +module config_module + use base_modules, only: wp, ip, max_name_len, cfd_config_base + + implicit none + public :: wp, ip, cfd_config, config_print, config_with_reconstruction + + ! 扩展配置类型 - 添加物理相关字段 + type, extends(cfd_config_base) :: cfd_config + ! 物理参数 + real(wp) :: left_boundary_value = 1.0_wp + real(wp) :: right_boundary_value = 2.0_wp + real(wp) :: domain_length = 2.0_wp + + ! 新增:物理模块相关配置 + real(wp) :: pulse_center = 0.5_wp ! 高斯脉冲中心 + real(wp) :: pulse_width = 0.1_wp ! 高斯脉冲宽度 + logical :: enable_physics = .true. ! 是否启用物理模块 + contains + ! 新增:物理相关配置方法 + procedure :: set_physics_parameters + procedure :: get_physics_info + end type cfd_config + +contains + + subroutine config_print(cfg) + type(cfd_config), intent(in) :: cfg + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(cfg%ic_type) + print *, "Reconstruction: ", trim(cfg%recon_scheme), " (order:", cfg%spatial_order, ")" + print *, "Flux type: ", trim(cfg%flux_type) + print *, "Time integration: RK", cfg%rk_order + print *, "Wave speed: ", cfg%wave_speed + print *, "Final time: ", cfg%final_time + print *, "Time step: ", cfg%dt + print *, "Boundary: ", trim(cfg%boundary_type) + + ! 新增:物理配置信息 + print *, "--- Physics Configuration ---" + print *, "Equation type: ", trim(cfg%equation_type) + print *, "Problem type: ", trim(cfg%problem_type) + print *, "Domain length: ", cfg%domain_length + print *, "Physics enabled: ", cfg%enable_physics + + if (cfg%ic_type == "gaussian") then + print *, "Pulse center: ", cfg%pulse_center + print *, "Pulse width: ", cfg%pulse_width + end if + + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(cfg, scheme, order) + type(cfd_config), intent(inout) :: cfg + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + integer :: i + + ! 转换为小写 + cfg%recon_scheme = scheme + do i = 1, len_trim(cfg%recon_scheme) + if (cfg%recon_scheme(i:i) >= 'A' .and. cfg%recon_scheme(i:i) <= 'Z') then + cfg%recon_scheme(i:i) = char(ichar(cfg%recon_scheme(i:i)) + 32) + end if + end do + + ! 设置阶数 + if (present(order)) then + cfg%spatial_order = order + else + if (index(cfg%recon_scheme, 'weno') > 0) then + cfg%spatial_order = 5 + else if (trim(cfg%recon_scheme) == 'eno') then + cfg%spatial_order = 3 + end if + end if + + if (cfg%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(cfg%recon_scheme), & + " Order: ", cfg%spatial_order + end if + end subroutine config_with_reconstruction + + ! ========== 新增:物理参数设置方法 ========== + + subroutine set_physics_parameters(this, equation_type, problem_type, & + domain_length, enable_physics) + class(cfd_config), intent(inout) :: this + character(len=*), intent(in), optional :: equation_type, problem_type + real(wp), intent(in), optional :: domain_length + logical, intent(in), optional :: enable_physics + + if (present(equation_type)) then + this%equation_type = trim(equation_type) + if (this%verbose) then + print *, "[CONFIG] Set equation type: ", trim(this%equation_type) + end if + end if + + if (present(problem_type)) then + this%problem_type = trim(problem_type) + if (this%verbose) then + print *, "[CONFIG] Set problem type: ", trim(this%problem_type) + end if + end if + + if (present(domain_length)) then + this%domain_length = domain_length + if (this%verbose) then + print *, "[CONFIG] Set domain length: ", this%domain_length + end if + end if + + if (present(enable_physics)) then + this%enable_physics = enable_physics + if (this%verbose) then + print *, "[CONFIG] Physics module enabled: ", this%enable_physics + end if + end if + end subroutine set_physics_parameters + + subroutine get_physics_info(this) + class(cfd_config), intent(in) :: this + + print *, "=== Physics Configuration Info ===" + print *, "Equation type: ", trim(this%equation_type) + print *, "Problem type: ", trim(this%problem_type) + print *, "Domain length: ", this%domain_length + print *, "Wave speed: ", this%wave_speed + print *, "Physics enabled: ", this%enable_physics + + if (this%ic_type == "gaussian") then + print *, "Pulse parameters:" + print *, " Center: ", this%pulse_center + print *, " Width: ", this%pulse_width + end if + + print *, "==================================" + end subroutine get_physics_info + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/infrastructure/domain.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/src/infrastructure/domain.f90 new file mode 100644 index 00000000..c3662f03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/infrastructure/domain.f90 @@ -0,0 +1,102 @@ +! src/infrastructure/domain.f90 +module domain_module + use base_modules, only: wp, ip, max_name_len + use config_module, only: cfd_config + use mesh_module, only: mesh_type + + implicit none + private + public :: wp, ip, domain_type, domain_create, is_physical_cell + + type :: domain_type + type(cfd_config), pointer :: config => null() + type(mesh_type), pointer :: mesh => null() + integer(ip) :: nghosts = 0 + integer(ip) :: ist = 1 ! 物理区域起始索引(1-based) + integer(ip) :: ied = 1 ! 物理区域结束索引(exclusive) + integer(ip) :: ntcells = 0 ! 总单元数(含ghost) + contains + procedure :: print_info => domain_print_info + procedure :: get_physical_indices => domain_get_physical_indices + end type domain_type + +contains + + function domain_create(config, mesh) result(domain) + type(cfd_config), target, intent(in) :: config + type(mesh_type), target, intent(in) :: mesh + type(domain_type) :: domain + + domain%config => config + domain%mesh => mesh + + ! 计算ghost层数(参考Julia的_calc_nghosts) + domain%nghosts = calc_nghosts(config) + domain%ist = domain%nghosts + 1 + domain%ied = domain%ist + mesh%ncells + domain%ntcells = mesh%ncells + 2 * domain%nghosts + + if (config%verbose) then + print *, "[DOMAIN] Created:" + print *, " Ghost layers: ", domain%nghosts + print *, " Physical cells: ", domain%ist, " to ", domain%ied - 1 + print *, " Total cells: ", domain%ntcells + end if + end function domain_create + + function calc_nghosts(config) result(nghosts) + type(cfd_config), intent(in) :: config + integer(ip) :: nghosts + + character(len=max_name_len) :: scheme + + scheme = config%recon_scheme + + if (scheme == "eno") then + nghosts = config%spatial_order + else if (index(scheme, "weno") > 0) then + nghosts = config%spatial_order / 2 + 1 + else + print *, "[WARNING] Unknown scheme, using default nghosts=2" + nghosts = 2 + end if + + if (nghosts <= 0) then + print *, "[ERROR] Invalid nghosts: ", nghosts + nghosts = 2 + end if + end function calc_nghosts + + logical function is_physical_cell(this, idx) + class(domain_type), intent(in) :: this + integer(ip), intent(in) :: idx + is_physical_cell = (idx >= this%ist .and. idx < this%ied) + end function is_physical_cell + + function domain_get_physical_indices(this) result(indices) + class(domain_type), intent(in) :: this + integer(ip), allocatable :: indices(:) + integer(ip) :: i, count + + count = this%ied - this%ist + allocate(indices(count)) + + do i = 1, count + indices(i) = this%ist + i - 1 + end do + end function domain_get_physical_indices + + subroutine domain_print_info(this) + class(domain_type), intent(in) :: this + + print *, "=== Domain Information ===" + print *, "Configuration: ", trim(this%config%recon_scheme), & + " order ", this%config%spatial_order + print *, "Ghost layers: ", this%nghosts + print *, "Physical cells: ", this%ist, " to ", this%ied - 1 + print *, "Total cells: ", this%ntcells + print *, "Mesh cells: ", this%mesh%ncells + print *, "==========================" + end subroutine domain_print_info + +end module domain_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..f810f3a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/infrastructure/mesh.f90 @@ -0,0 +1,73 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use base_modules, only: wp, ip + + implicit none + public :: wp, ip, mesh_type, mesh_init, mesh_print_info + + ! 网格类型 + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer(ip) :: ncells = 40 + integer(ip) :: nnodes + integer(ip) :: nx + real(wp), allocatable :: x(:) ! 节点坐标 + real(wp), allocatable :: xcc(:) ! 单元中心坐标 + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer(ip), optional, intent(in) :: ncells + + integer(ip) :: i + + ! 设置参数 + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! 计算 + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! 分配内存 + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! 生成节点坐标 + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! 生成单元中心坐标 + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== Mesh Information ===" + print *, "Domain: [", this%xmin, ", ", this%xmax, "]" + print *, "Cells: ", this%ncells + print *, "Nodes: ", this%nnodes + print *, "dx: ", this%dx + print *, "L: ", this%L + print *, "========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/infrastructure/solution.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/src/infrastructure/solution.f90 new file mode 100644 index 00000000..ce88fd8a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/infrastructure/solution.f90 @@ -0,0 +1,131 @@ +! src/infrastructure/solution.f90 +module solution_module + use base_modules, only: wp, ip + use domain_module, only: domain_type + + implicit none + private + public :: wp, ip, solution_type, solution_create, solution_reset + + type :: solution_type + type(domain_type), pointer :: domain => null() + real(wp), allocatable :: u(:) ! 当前解(含ghost) + real(wp), allocatable :: un(:) ! 旧解 + real(wp), allocatable :: q_face_left(:) ! 左界面值 + real(wp), allocatable :: q_face_right(:)! 右界面值 + real(wp), allocatable :: flux(:) ! 通量 + real(wp), allocatable :: res(:) ! 残差 + contains + procedure :: initialize => solution_initialize + procedure :: update_old_field => solution_update_old_field + procedure :: print_info => solution_print_info + procedure :: reset => solution_reset_instance + end type solution_type + +contains + + function solution_create(domain) result(solution) + type(domain_type), target, intent(in) :: domain + type(solution_type) :: solution + + integer(ip) :: ncells, nnodes, ntcells + + solution%domain => domain + + ncells = domain%mesh%ncells + nnodes = domain%mesh%nnodes + ntcells = domain%ntcells + + ! 分配数组(与Julia solution.jl一致) + allocate(solution%u(ntcells), source=0.0_wp) + allocate(solution%un(ntcells), source=0.0_wp) + allocate(solution%q_face_left(nnodes), source=0.0_wp) + allocate(solution%q_face_right(nnodes), source=0.0_wp) + allocate(solution%flux(nnodes), source=0.0_wp) + allocate(solution%res(ncells), source=0.0_wp) + + if (domain%config%verbose) then + print *, "[SOLUTION] Created:" + print *, " u size: ", size(solution%u), " (with ghosts)" + print *, " flux size: ", size(solution%flux) + print *, " res size: ", size(solution%res) + end if + end function solution_create + + subroutine solution_initialize(this, initial_values) + class(solution_type), intent(inout) :: this + real(wp), intent(in), optional :: initial_values(:) + + integer(ip) :: i, idx + type(domain_type), pointer :: domain + + domain => this%domain + + if (present(initial_values)) then + ! 应用初始值到物理区域 + do i = domain%ist, domain%ied - 1 + idx = i - domain%ist + 1 + if (idx <= size(initial_values)) then + this%u(i) = initial_values(idx) + end if + end do + else + ! 默认为0 + this%u = 0.0_wp + end if + + ! 同步旧场(与Julia的update_old_field一致) + call this%update_old_field() + + if (domain%config%verbose) then + print *, "[SOLUTION] Initialized" + print *, " u range: ", minval(this%u), " to ", maxval(this%u) + end if + end subroutine solution_initialize + + subroutine solution_update_old_field(this) + class(solution_type), intent(inout) :: this + this%un = this%u ! 与Julia的 un .= u 一致 + end subroutine solution_update_old_field + + subroutine solution_reset_instance(this) + class(solution_type), intent(inout) :: this + call solution_reset(this) + end subroutine solution_reset_instance + + subroutine solution_reset(solution) + type(solution_type), intent(inout) :: solution + + if (allocated(solution%u)) solution%u = 0.0_wp + if (allocated(solution%un)) solution%un = 0.0_wp + if (allocated(solution%q_face_left)) solution%q_face_left = 0.0_wp + if (allocated(solution%q_face_right)) solution%q_face_right = 0.0_wp + if (allocated(solution%flux)) solution%flux = 0.0_wp + if (allocated(solution%res)) solution%res = 0.0_wp + + if (associated(solution%domain) .and. solution%domain%config%verbose) then + print *, "[SOLUTION] Reset" + end if + end subroutine solution_reset + + subroutine solution_print_info(this) + class(solution_type), intent(in) :: this + + print *, "=== Solution Information ===" + print *, "Arrays:" + print *, " u: ", size(this%u), " elements" + print *, " un: ", size(this%un), " elements" + print *, " q_face_left: ", size(this%q_face_left), " elements" + print *, " q_face_right: ", size(this%q_face_right), " elements" + print *, " flux: ", size(this%flux), " elements" + print *, " res: ", size(this%res), " elements" + + if (allocated(this%u)) then + print *, "Values:" + print *, " u min/max: ", minval(this%u), maxval(this%u) + print *, " un min/max: ", minval(this%un), maxval(this%un) + end if + print *, "============================" + end subroutine solution_print_info + +end module solution_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/manager/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03c/src/manager/CMakeLists.txt new file mode 100644 index 00000000..00c8bf49 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/manager/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/manager/CMakeLists.txt +message(STATUS "配置管理器模块...") + +# 创建管理器库 +add_library(manager STATIC + component_manager.f90 + component_factory.f90 +) + +# 明确依赖关系:管理器依赖所有其他模块 +target_link_libraries(manager + PRIVATE + core + infrastructure + reconstructor + flux +) + +# 设置模块输出目录 +set_target_properties(manager PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "管理器模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/manager/component_factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/src/manager/component_factory.f90 new file mode 100644 index 00000000..de8cbf1a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/manager/component_factory.f90 @@ -0,0 +1,142 @@ +! src/manager/component_factory.f90 (完整文件) +module component_factory_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + use eno_reconstructor_module, only: eno_reconstructor, create_eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor, create_weno3_reconstructor + use weno5_reconstructor_module, only: weno5_reconstructor, create_weno5_reconstructor + use rusanov_flux_module, only: rusanov_flux, create_rusanov_flux + + implicit none + private + public :: wp, create_reconstructor, create_flux_calculator + + ! 错误代码 + integer, parameter :: CM_SUCCESS = 0 + integer, parameter :: CM_ERROR_UNKNOWN_SCHEME = 1 + integer, parameter :: CM_ERROR_UNKNOWN_FLUX = 2 + integer, parameter :: CM_ERROR_INVALID_ORDER = 3 + +contains + + ! ==================== 重构器创建 ==================== + + function create_reconstructor(config, status) result(recon) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(reconstructor_base), allocatable :: recon + + character(len=20) :: scheme + integer :: order, error_code + + scheme = trim(adjustl(config%recon_scheme)) + order = config%spatial_order + + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating reconstructor: ", scheme, " order=", order + end if + + ! 处理"weno"作为WENO5的别名(与Julia一致) + if (scheme == "weno" .and. order == 5) then + scheme = "weno5" + end if + + select case(scheme) + case('eno') + allocate(eno_reconstructor :: recon) + select type(recon) + type is(eno_reconstructor) + recon = create_eno_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case('weno3') + allocate(weno3_reconstructor :: recon) + select type(recon) + type is(weno3_reconstructor) + recon = create_weno3_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case('weno5') + allocate(weno5_reconstructor :: recon) + select type(recon) + type is(weno5_reconstructor) + recon = create_weno5_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case default + error_code = CM_ERROR_UNKNOWN_SCHEME + if (config%verbose) then + print *, "[ERROR] Unknown reconstructor scheme: ", scheme + print *, " Available: eno, weno3, weno5" + end if + end select + + ! 检查阶数有效性 + if (error_code == CM_SUCCESS) then + if (order < 1) then + error_code = CM_ERROR_INVALID_ORDER + if (config%verbose) then + print *, "[ERROR] Invalid spatial order: ", order + end if + end if + end if + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Reconstructor creation failed" + end if + end function create_reconstructor + + ! ==================== 通量计算器创建 ==================== + + function create_flux_calculator(config, status) result(flux) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(flux_calculator_base), allocatable :: flux + + character(len=20) :: flux_type + integer :: error_code + + flux_type = trim(adjustl(config%flux_type)) + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating flux calculator: ", flux_type + end if + + select case(flux_type) + case('rusanov') + allocate(rusanov_flux :: flux) + select type(flux) + type is(rusanov_flux) + flux = create_rusanov_flux() + flux%wave_speed_default = config%wave_speed + end select + + case default + error_code = CM_ERROR_UNKNOWN_FLUX + if (config%verbose) then + print *, "[ERROR] Unknown flux type: ", flux_type + print *, " Available: rusanov" + end if + end select + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Flux calculator creation failed" + end if + end function create_flux_calculator + +end module component_factory_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/manager/component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/src/manager/component_manager.f90 new file mode 100644 index 00000000..25eac29b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/manager/component_manager.f90 @@ -0,0 +1,76 @@ +! src/manager/component_manager.f90 (完整文件) +module component_manager_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use component_factory_module, only: create_reconstructor, create_flux_calculator + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + implicit none + private + public :: wp, component_manager_info, validate_config + public :: create_reconstructor, create_flux_calculator + +contains + + ! ==================== 配置验证 ==================== + + function validate_config(config) result(is_valid) + type(cfd_config), intent(in) :: config + logical :: is_valid + + integer :: status + class(reconstructor_base), allocatable :: test_recon + class(flux_calculator_base), allocatable :: test_flux + + is_valid = .false. + + ! 测试创建重构器 + test_recon = create_reconstructor(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid reconstructor configuration" + end if + return + end if + + ! 测试创建通量计算器 + test_flux = create_flux_calculator(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid flux configuration" + end if + return + end if + + ! 清理测试组件 + if (allocated(test_recon)) deallocate(test_recon) + if (allocated(test_flux)) deallocate(test_flux) + + is_valid = .true. + + if (config%verbose) then + print *, "[CONFIG VALIDATION] Configuration is valid" + end if + end function validate_config + + ! ==================== 信息显示 ==================== + + subroutine component_manager_info() + print *, "=== Component Manager ===" + print *, "Available reconstructors:" + print *, " - eno (orders: 1-7)" + print *, " - weno3 (order: 3)" + print *, " - weno5 (order: 5)" + print *, "" + print *, "Available flux calculators:" + print *, " - rusanov" + print *, "" + print *, "Features:" + print *, " - Configuration validation" + print *, " - Component creation from config" + print *, " - Error handling with status codes" + print *, "=========================" + end subroutine component_manager_info + +end module component_manager_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..3fde7746 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,28 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux STATIC + base.f90 + rusanov.f90 +) + +#target_link_libraries(flux PRIVATE core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 明确设置不使用 /Qm64 选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + set_target_properties(flux PROPERTIES + Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /Qm64" # 明确设置,避免继承问题 + ) +endif() + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/flux/base.f90 new file mode 100644 index 00000000..7080a7ae --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/flux/base.f90 @@ -0,0 +1,30 @@ +!src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + procedure :: print_basic_info => flux_print_basic ! 添加辅助方法 + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + + subroutine flux_print_basic(this) + class(flux_calculator_base), intent(in) :: this + print *, " Name: ", trim(this%name) + end subroutine flux_print_basic + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..daa9e3bb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/flux/rusanov.f90 @@ -0,0 +1,41 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux, create_rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + + ! 构造函数接口 + interface rusanov_flux + module procedure create_rusanov_flux + end interface + +contains + + ! 构造函数 + type(rusanov_flux) function create_rusanov_flux() result(this) + this%name = "Rusanov" + this%wave_speed_default = 1.0_real64 + end function create_rusanov_flux + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Flux calculator information:" + call this%print_basic_info() + + ! 添加Rusanov特有信息 + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..c88ea647 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,23 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor STATIC + base.f90 + eno.f90 + weno3.f90 + weno5.f90 +) + +target_link_libraries(reconstructor PRIVATE core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..53798d02 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/reconstructor/base.f90 @@ -0,0 +1,33 @@ +!src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + procedure :: print_basic_info => reconstructor_print_basic ! 添加一个辅助方法 + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + + subroutine reconstructor_print_basic(this) + class(reconstructor_base), intent(in) :: this + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_print_basic + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..f973e8b3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor, create_eno_reconstructor ! ← 添加这个 + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + + ! 构造函数接口 + interface eno_reconstructor + module procedure create_eno_reconstructor + end interface + +contains + + ! 构造函数 + type(eno_reconstructor) function create_eno_reconstructor() result(this) + this%name = "ENO" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_eno_reconstructor + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加ENO特有信息 + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..d5b7a747 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno3_reconstructor, create_weno3_reconstructor + + type, extends(reconstructor_base) :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + + ! 构造函数接口 + interface weno3_reconstructor + module procedure create_weno3_reconstructor + end interface + +contains + + ! 构造函数 + type(weno3_reconstructor) function create_weno3_reconstructor() result(this) + this%name = "WENO3" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_weno3_reconstructor + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO3特有信息 + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/reconstructor/weno5.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/reconstructor/weno5.f90 new file mode 100644 index 00000000..a869c67d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/numerics/reconstructor/weno5.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/weno5.f90(新增) +module weno5_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno5_reconstructor, create_weno5_reconstructor + + type, extends(reconstructor_base) :: weno5_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno5_info + end type weno5_reconstructor + + ! 构造函数接口 + interface weno5_reconstructor + module procedure create_weno5_reconstructor + end interface + +contains + + ! 构造函数 + type(weno5_reconstructor) function create_weno5_reconstructor() result(this) + this%name = "WENO5" + this%order = 5 + this%epsilon = 1.0e-6_real64 + end function create_weno5_reconstructor + + subroutine weno5_info(this) + class(weno5_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO5特有信息 + print *, " Type: WENO-5 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno5_info + +end module weno5_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/physics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03c/src/physics/CMakeLists.txt new file mode 100644 index 00000000..cc4e233a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/physics/CMakeLists.txt @@ -0,0 +1,19 @@ +# src/physics/CMakeLists.txt +message(STATUS "配置物理模块...") + +# 创建物理模块库 +add_library(physics STATIC + physics_interface.f90 + equations/linear_convection.f90 + problems/linear_convection_problem.f90 +) + +# 链接依赖 +target_link_libraries(physics PRIVATE base) + +# 设置模块输出目录 +set_target_properties(physics PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "物理模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/physics/equations/linear_convection.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/src/physics/equations/linear_convection.f90 new file mode 100644 index 00000000..fff7be55 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/physics/equations/linear_convection.f90 @@ -0,0 +1,49 @@ +! src/physics/equations/linear_convection.f90 +module linear_convection_equation + use precision_module, only: wp, ip + use physics_interface, only: physics_equation + implicit none + private + + ! 具体方程类型 - 先声明 + type, extends(physics_equation) :: linear_convection_eq + real(wp) :: wave_speed = 1.0_wp + contains + procedure :: flux => lc_flux + procedure :: speed => lc_speed + end type linear_convection_eq + + ! 公开接口 + public :: wp, ip + public :: linear_convection_eq, create_linear_convection_eq + +contains + + ! 构造函数 + function create_linear_convection_eq(wave_speed) result(eq) + real(wp), intent(in), optional :: wave_speed + type(linear_convection_eq) :: eq + + eq%name = "Linear Convection" + if (present(wave_speed)) then + eq%wave_speed = wave_speed + else + eq%wave_speed = 1.0_wp + end if + end function create_linear_convection_eq + + ! 方法实现 + pure function lc_flux(this, u) result(f) + class(linear_convection_eq), intent(in) :: this + real(wp), intent(in) :: u + real(wp) :: f + f = this%wave_speed * u + end function lc_flux + + pure function lc_speed(this) result(a) + class(linear_convection_eq), intent(in) :: this + real(wp) :: a + a = this%wave_speed + end function lc_speed + +end module linear_convection_equation \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/physics/physics_interface.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/src/physics/physics_interface.f90 new file mode 100644 index 00000000..45002da6 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/physics/physics_interface.f90 @@ -0,0 +1,64 @@ +! src/physics/physics_interface.f90 +module physics_interface + use precision_module, only: wp, ip + implicit none + private + + ! 定义抽象基类型 - 先声明为私有,然后在公开部分导出 + type, abstract :: physics_equation + character(len=:), allocatable :: name + contains + procedure(eq_flux_abs), deferred :: flux + procedure(eq_speed_abs), deferred :: speed + end type physics_equation + + type, abstract :: physics_problem + character(len=:), allocatable :: name + contains + procedure(prob_ic_abs), deferred :: initial_condition + procedure(prob_bc_abs), deferred :: boundary_condition + procedure(prob_exact_abs), deferred :: exact_solution + end type physics_problem + + ! 抽象接口定义 + abstract interface + pure function eq_flux_abs(this, u) result(f) + import :: physics_equation, wp + class(physics_equation), intent(in) :: this + real(wp), intent(in) :: u + real(wp) :: f + end function eq_flux_abs + + pure function eq_speed_abs(this) result(a) + import :: physics_equation, wp + class(physics_equation), intent(in) :: this + real(wp) :: a + end function eq_speed_abs + + subroutine prob_ic_abs(this, x, u) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp), intent(out) :: u(:) + end subroutine prob_ic_abs + + subroutine prob_bc_abs(this, u, t) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(inout) :: u(:) + real(wp), intent(in), optional :: t + end subroutine prob_bc_abs + + function prob_exact_abs(this, x, t) result(u) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(in) :: x(:), t + real(wp), dimension(size(x)) :: u + end function prob_exact_abs + end interface + + ! 公开接口 - 使用独立的public语句 + public :: wp, ip + public :: physics_equation, physics_problem + +end module physics_interface \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/physics/problems/linear_convection_problem.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/src/physics/problems/linear_convection_problem.f90 new file mode 100644 index 00000000..06226ed1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/physics/problems/linear_convection_problem.f90 @@ -0,0 +1,118 @@ +! src/physics/problems/linear_convection_problem.f90 +module linear_convection_problem + use precision_module, only: wp, ip + use physics_interface, only: physics_problem + implicit none + private + + ! 具体问题类型 - 先声明 + type, extends(physics_problem) :: linear_convection_prob + real(wp) :: wave_speed = 1.0_wp + real(wp) :: domain_length = 2.0_wp + character(len=20) :: ic_type = "step" + character(len=20) :: boundary_type = "periodic" + contains + procedure :: initial_condition => lc_initial_condition + procedure :: boundary_condition => lc_boundary_condition + procedure :: exact_solution => lc_exact_solution + end type linear_convection_prob + + ! 公开接口 + public :: wp, ip + public :: linear_convection_prob, create_linear_convection_prob + +contains + + ! 构造函数 + function create_linear_convection_prob(wave_speed, domain_length, & + ic_type, boundary_type) result(prob) + real(wp), intent(in), optional :: wave_speed, domain_length + character(len=*), intent(in), optional :: ic_type, boundary_type + type(linear_convection_prob) :: prob + + prob%name = "Linear Convection Problem" + + if (present(wave_speed)) prob%wave_speed = wave_speed + if (present(domain_length)) prob%domain_length = domain_length + if (present(ic_type)) prob%ic_type = ic_type + if (present(boundary_type)) prob%boundary_type = boundary_type + end function create_linear_convection_prob + + ! 初始条件 + subroutine lc_initial_condition(this, x, u) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp), intent(out) :: u(:) + + integer :: i + + select case (trim(this%ic_type)) + case ("step") + do i = 1, size(x) + if (x(i) >= 0.5_wp .and. x(i) <= 1.0_wp) then + u(i) = 2.0_wp + else + u(i) = 1.0_wp + end if + end do + + case ("sin", "sine") + do i = 1, size(x) + u(i) = sin(2.0_wp * 3.141592653589793_wp * x(i) / this%domain_length) + end do + + case ("gaussian") + do i = 1, size(x) + u(i) = exp(-((x(i) - 0.5_wp) / 0.1_wp)**2) + end do + + case default + ! 默认阶跃函数 + do i = 1, size(x) + if (x(i) >= 0.5_wp .and. x(i) <= 1.0_wp) then + u(i) = 2.0_wp + else + u(i) = 1.0_wp + end if + end do + end select + end subroutine lc_initial_condition + + ! 边界条件(虚拟实现,实际在boundary模块) + subroutine lc_boundary_condition(this, u, t) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(inout) :: u(:) + real(wp), intent(in), optional :: t + + ! 边界条件将在独立模块实现 + print *, "[PROBLEM] Boundary condition placeholder" + if (present(t)) then + print *, " Time = ", t + end if + end subroutine lc_boundary_condition + + ! 精确解(周期性平移) + function lc_exact_solution(this, x, t) result(u) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(in) :: x(:), t + real(wp), dimension(size(x)) :: u + real(wp), dimension(size(x)) :: x_shifted + integer :: i + + ! 周期性平移 + do i = 1, size(x) + x_shifted(i) = x(i) - this%wave_speed * t + ! 确保在 [0, domain_length) 范围内 + do while (x_shifted(i) < 0.0_wp) + x_shifted(i) = x_shifted(i) + this%domain_length + end do + do while (x_shifted(i) >= this%domain_length) + x_shifted(i) = x_shifted(i) - this%domain_length + end do + end do + + ! 重用初始条件函数 + call this%initial_condition(x_shifted, u) + end function lc_exact_solution + +end module linear_convection_problem \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/run_eno_weno.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/src/run_eno_weno.f90 new file mode 100644 index 00000000..5821c5a0 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/run_eno_weno.f90 @@ -0,0 +1,171 @@ +! src/run_eno_weno.f90 +program run_eno_weno + ! 使用所有需要的模块 + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction, config_print + use mesh_module, only: mesh_type + use solver_base_module, only: SOLVER_INITIALIZED, SOLVER_COMPLETED + + use physics_solver_module, only: physics_solver + use registry_module, only: registry_init, registry_cleanup + + implicit none + + type(cfd_config) :: config_eno3, config_weno3, config_weno5 + type(mesh_type) :: mesh + type(physics_solver) :: solver_eno3, solver_weno3, solver_weno5 + + character(len=100) :: output_file + integer :: status + + print *, "=== ENO/WENO对比分析 (Fortran版本) ===" + print *, "" + + ! 初始化注册系统 + call registry_init(verbose=.true.) + print *, "" + + ! 步骤1: 初始化网格 + print *, "步骤1: 初始化网格..." + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=40) + call mesh%print_info() + print *, "" + + ! 步骤2: 配置并运行ENO3求解器 + print *, "步骤2: 运行ENO3求解器..." + print *, "-------------------------" + + ! 创建ENO3配置 + config_eno3%verbose = .true. + call config_with_reconstruction(config_eno3, "eno", 3) + config_eno3%dt = 0.0025_wp + config_eno3%rk_order = 2 + config_eno3%wave_speed = 1.0_wp + config_eno3%final_time = 0.625_wp + config_eno3%ic_type = "step" + + print *, "ENO3配置:" + call config_print(config_eno3) + + ! 创建ENO3求解器 + print *, "创建ENO3求解器..." + solver_eno3 = physics_solver(config_eno3, mesh) + call solver_eno3%print_info() + print *, "" + + ! 初始化并运行ENO3求解器 + print *, "运行ENO3求解器..." + call solver_eno3%initialize() + call solver_eno3%run_to_time(config_eno3%final_time) + + if (solver_eno3%get_state() == SOLVER_COMPLETED) then + print *, "✓ ENO3求解器运行完成" + print *, " 最终时间: ", solver_eno3%current_time + print *, " 总步数: ", solver_eno3%current_step + else + print *, "✗ ENO3求解器运行失败" + print *, " 错误信息: ", trim(solver_eno3%get_error()) + end if + print *, "" + + ! 步骤3: 配置并运行WENO3求解器 + print *, "步骤3: 运行WENO3求解器..." + print *, "--------------------------" + + ! 创建WENO3配置 + config_weno3%verbose = .true. + call config_with_reconstruction(config_weno3, "weno3", 3) + config_weno3%dt = 0.0025_wp + config_weno3%rk_order = 2 + config_weno3%wave_speed = 1.0_wp + config_weno3%final_time = 0.625_wp + config_weno3%ic_type = "step" + + print *, "WENO3配置:" + call config_print(config_weno3) + + ! 创建WENO3求解器 + print *, "创建WENO3求解器..." + solver_weno3 = physics_solver(config_weno3, mesh) + call solver_weno3%print_info() + print *, "" + + ! 初始化并运行WENO3求解器 + print *, "运行WENO3求解器..." + call solver_weno3%initialize() + call solver_weno3%run_to_time(config_weno3%final_time) + + if (solver_weno3%get_state() == SOLVER_COMPLETED) then + print *, "✓ WENO3求解器运行完成" + print *, " 最终时间: ", solver_weno3%current_time + print *, " 总步数: ", solver_weno3%current_step + else + print *, "✗ WENO3求解器运行失败" + print *, " 错误信息: ", trim(solver_weno3%get_error()) + end if + print *, "" + + ! 步骤4: 配置并运行WENO5求解器 + print *, "步骤4: 运行WENO5求解器..." + print *, "--------------------------" + + ! 创建WENO5配置 + config_weno5%verbose = .true. + call config_with_reconstruction(config_weno5, "weno", 5) + config_weno5%dt = 0.0025_wp + config_weno5%rk_order = 2 + config_weno5%wave_speed = 1.0_wp + config_weno5%final_time = 0.625_wp + config_weno5%ic_type = "step" + + print *, "WENO5配置:" + call config_print(config_weno5) + + ! 创建WENO5求解器 + print *, "创建WENO5求解器..." + solver_weno5 = physics_solver(config_weno5, mesh) + call solver_weno5%print_info() + print *, "" + + ! 初始化并运行WENO5求解器 + print *, "运行WENO5求解器..." + call solver_weno5%initialize() + call solver_weno5%run_to_time(config_weno5%final_time) + + if (solver_weno5%get_state() == SOLVER_COMPLETED) then + print *, "✓ WENO5求解器运行完成" + print *, " 最终时间: ", solver_weno5%current_time + print *, " 总步数: ", solver_weno5%current_step + else + print *, "✗ WENO5求解器运行失败" + print *, " 错误信息: ", trim(solver_weno5%get_error()) + end if + print *, "" + + ! 清理注册系统 + call registry_cleanup() + + ! 清理求解器 + call solver_eno3%cleanup() + call solver_weno3%cleanup() + call solver_weno5%cleanup() + + print *, "=== 分析完成 ===" + print *, "总结:" + print *, " ENO3: 时间=", solver_eno3%current_time, " 步数=", solver_eno3%current_step + print *, " WENO3: 时间=", solver_weno3%current_time, " 步数=", solver_weno3%current_step + print *, " WENO5: 时间=", solver_weno5%current_time, " 步数=", solver_weno5%current_step + + if (solver_eno3%get_state() == SOLVER_COMPLETED .and. & + solver_weno3%get_state() == SOLVER_COMPLETED .and. & + solver_weno5%get_state() == SOLVER_COMPLETED) then + print *, "" + print *, "✓ 所有求解器成功运行!" + print *, "下一步: 添加边界条件模块" + else + print *, "" + print *, "✗ 有求解器运行失败" + print *, "需要先调试现有代码" + end if + +end program run_eno_weno \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/solver/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03c/src/solver/CMakeLists.txt new file mode 100644 index 00000000..f8499eec --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/solver/CMakeLists.txt @@ -0,0 +1,21 @@ +# src/solver/CMakeLists.txt +message(STATUS "配置求解器模块...") + +add_library(solver STATIC + base.f90 + physics_solver.f90 +) + +target_link_libraries(solver + PRIVATE + infrastructure + core + physics + manager +) + +set_target_properties(solver PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "求解器模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/solver/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/src/solver/base.f90 new file mode 100644 index 00000000..cfd78c47 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/solver/base.f90 @@ -0,0 +1,264 @@ +! src/solver/base.f90 +module solver_base_module + use base_modules, only: wp => wp, ip => ip ! 重命名以避免冲突 + + use config_module, only: cfd_config + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create + + implicit none + private + + ! 明确导出列表 + public :: wp, ip ! 类型参数 + public :: solver_base, create_solver_base ! 类型和构造函数 + public :: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, SOLVER_RUNNING + public :: SOLVER_COMPLETED, SOLVER_ERROR ! 状态常量 + + ! 求解器状态枚举 + integer, parameter :: SOLVER_UNINITIALIZED = 0 + integer, parameter :: SOLVER_INITIALIZED = 1 + integer, parameter :: SOLVER_RUNNING = 2 + integer, parameter :: SOLVER_COMPLETED = 3 + integer, parameter :: SOLVER_ERROR = 4 + + ! 求解器基类 + type :: solver_base + ! 基本组件 + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + + ! 状态管理 + integer :: state = SOLVER_UNINITIALIZED + character(len=100) :: error_message = "" + real(wp) :: current_time = 0.0_wp + integer(ip) :: current_step = 0 + + ! 时间控制 + real(wp) :: dt_original = 0.0_wp + contains + procedure :: initialize => solver_base_initialize + procedure :: step => solver_base_step + procedure :: run_to_time => solver_base_run_to_time + procedure :: cleanup => solver_base_cleanup + procedure :: get_state => solver_base_get_state + procedure :: get_error => solver_base_get_error + procedure :: print_info => solver_base_print_info + end type solver_base + + ! 构造函数接口 + interface solver_base + module procedure create_solver_base + end interface + +contains + + ! ==================== 构造函数 ==================== + + function create_solver_base(config, mesh) result(solver) + type(cfd_config), intent(in) :: config + type(mesh_type), intent(in) :: mesh + type(solver_base) :: solver + + solver%config = config + solver%mesh = mesh + + ! 创建域 + solver%domain = domain_create(config, mesh) + + ! 创建解 + solver%solution = solution_create(solver%domain) + + ! 保存原始时间步长 + solver%dt_original = config%dt + + if (config%verbose) then + print *, "[SOLVER] Base solver created" + print *, " Mesh cells: ", mesh%ncells + print *, " Domain total cells: ", solver%domain%ntcells + end if + end function create_solver_base + + ! ==================== 初始化 ==================== + + subroutine solver_base_initialize(this) + class(solver_base), intent(inout) :: this + + if (this%state == SOLVER_INITIALIZED) then + if (this%config%verbose) then + print *, "[SOLVER] Already initialized" + end if + return + end if + + ! 初始化解(通过配置) + ! 这里暂时简化,实际需要调用初始条件工厂 + print *, "[INFO] Base solver initialized (simplified)" + + ! 更新状态 + this%state = SOLVER_INITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + + if (this%config%verbose) then + print *, "[SOLVER] Initialized at t = ", this%current_time + end if + end subroutine solver_base_initialize + + ! ==================== 单步计算(虚方法) ==================== + + subroutine solver_base_step(this, dt) + class(solver_base), intent(inout) :: this + real(wp), intent(in) :: dt + + ! 基类中这只是虚方法,需要在子类中实现 + print *, "[INFO] Base solver step (virtual method)" + print *, " dt = ", dt + print *, " t = ", this%current_time + + ! 更新时间 + this%current_time = this%current_time + dt + this%current_step = this%current_step + 1 + + ! 简单模拟:只是更新状态 + if (this%config%verbose) then + print *, "[SOLVER] Step completed: t = ", this%current_time, & + ", step = ", this%current_step + end if + end subroutine solver_base_step + + ! ==================== 运行到指定时间 ==================== + + subroutine solver_base_run_to_time(this, final_time) + class(solver_base), intent(inout) :: this + real(wp), intent(in) :: final_time + + real(wp) :: dt, t_remaining + integer :: step_count + + if (this%state /= SOLVER_INITIALIZED) then + this%error_message = "Solver not initialized" + this%state = SOLVER_ERROR + if (this%config%verbose) then + print *, "[SOLVER BASE ERROR] Not initialized: ", trim(this%error_message) + end if + return + end if + + this%state = SOLVER_RUNNING + step_count = 0 + + if (this%config%verbose) then + print *, "[SOLVER BASE] Running from t = ", this%current_time, & + " to t = ", final_time + print *, " Time step: ", this%config%dt + end if + + do while (this%current_time < final_time - 1e-12_wp) + ! 计算时间步长 + t_remaining = final_time - this%current_time + dt = min(this%config%dt, t_remaining) + + ! 执行时间步 + call this%step(dt) + + step_count = step_count + 1 + + ! 每50步输出一次进度 + if (mod(step_count, 50) == 0 .and. this%config%verbose) then + print *, "[SOLVER BASE] Progress: t = ", this%current_time, & + " / ", final_time, " (step ", step_count, ")" + end if + end do + + ! 恢复原始时间步长 + this%config%dt = this%dt_original + + ! 更新状态 + this%state = SOLVER_COMPLETED + + if (this%config%verbose) then + print *, "[SOLVER BASE] Run completed:" + print *, " Final time: ", this%current_time + print *, " Total steps: ", this%current_step + print *, " State: ", this%state + end if + end subroutine solver_base_run_to_time + + ! ==================== 清理 ==================== + + subroutine solver_base_cleanup(this) + class(solver_base), intent(inout) :: this + + ! 重置状态 + this%state = SOLVER_UNINITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + this%error_message = "" + + if (this%config%verbose) then + print *, "[SOLVER] Cleaned up" + end if + end subroutine solver_base_cleanup + + ! ==================== 状态查询 ==================== + + function solver_base_get_state(this) result(state) + class(solver_base), intent(in) :: this + integer :: state + state = this%state + end function solver_base_get_state + + function solver_base_get_error(this) result(error_msg) + class(solver_base), intent(in) :: this + character(len=100) :: error_msg + error_msg = trim(this%error_message) + end function solver_base_get_error + + ! ==================== 信息打印 ==================== + + subroutine solver_base_print_info(this) + class(solver_base), intent(in) :: this + + character(len=20) :: state_str + + ! 状态字符串 + select case (this%state) + case (SOLVER_UNINITIALIZED) + state_str = "Uninitialized" + case (SOLVER_INITIALIZED) + state_str = "Initialized" + case (SOLVER_RUNNING) + state_str = "Running" + case (SOLVER_COMPLETED) + state_str = "Completed" + case (SOLVER_ERROR) + state_str = "Error" + case default + state_str = "Unknown" + end select + + print *, "=== Solver Information ===" + print *, "State: ", trim(state_str) + print *, "Current time: ", this%current_time + print *, "Current step: ", this%current_step + print *, "Error message: '", trim(this%error_message), "'" + + ! 配置信息 + print *, "Configuration:" + print *, " Scheme: ", trim(this%config%recon_scheme) + print *, " Order: ", this%config%spatial_order + print *, " dt: ", this%config%dt + + ! 域信息 + print *, "Domain:" + print *, " Ghost layers: ", this%domain%nghosts + print *, " Physical cells: ", this%domain%ist, " to ", this%domain%ied - 1 + + print *, "=========================" + end subroutine solver_base_print_info + +end module solver_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/src/solver/physics_solver.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/src/solver/physics_solver.f90 new file mode 100644 index 00000000..f41ac89f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/src/solver/physics_solver.f90 @@ -0,0 +1,503 @@ +! src/solver/physics_solver.f90 (简化版) +module physics_solver_module + use base_modules, only: wp => wp, ip => ip + use config_module, only: cfd_config + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create + + use physics_interface, only: physics_equation, physics_problem + use linear_convection_equation, only: linear_convection_eq, create_linear_convection_eq + use linear_convection_problem, only: linear_convection_prob, create_linear_convection_prob + + use component_manager_module, only: create_reconstructor, create_flux_calculator + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + implicit none + private + + ! 明确导出列表 + public :: wp, ip, physics_solver, create_physics_solver + public :: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, SOLVER_RUNNING + public :: SOLVER_COMPLETED, SOLVER_ERROR + + ! 求解器状态枚举 + integer, parameter :: SOLVER_UNINITIALIZED = 0 + integer, parameter :: SOLVER_INITIALIZED = 1 + integer, parameter :: SOLVER_RUNNING = 2 + integer, parameter :: SOLVER_COMPLETED = 3 + integer, parameter :: SOLVER_ERROR = 4 + + ! 物理求解器类型(不继承,独立实现) + type :: physics_solver + ! 基本组件 + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + + ! 物理组件 + class(physics_equation), allocatable :: equation + class(physics_problem), allocatable :: problem + + ! 数值组件 + class(reconstructor_base), allocatable :: reconstructor + class(flux_calculator_base), allocatable :: flux_calculator + + ! 状态管理 + integer :: state = SOLVER_UNINITIALIZED + character(len=100) :: error_message = "" + real(wp) :: current_time = 0.0_wp + integer(ip) :: current_step = 0 + + ! 时间控制 + real(wp) :: dt_original = 0.0_wp + logical :: physics_initialized = .false. + contains + procedure :: initialize => physics_solver_initialize + procedure :: step => physics_solver_step + procedure :: run_to_time => physics_solver_run_to_time + procedure :: cleanup => physics_solver_cleanup + procedure :: get_state => physics_solver_get_state + procedure :: get_error => physics_solver_get_error + procedure :: print_info => physics_solver_print_info + procedure, private :: create_physics_components + procedure, private :: create_numerical_components + end type physics_solver + + ! 构造函数接口 + interface physics_solver + module procedure create_physics_solver + end interface + +contains + + ! ==================== 构造函数 ==================== + + function create_physics_solver(config, mesh) result(solver) + type(cfd_config), intent(in) :: config + type(mesh_type), intent(in) :: mesh + type(physics_solver) :: solver + + solver%config = config + solver%mesh = mesh + + ! 创建域 + solver%domain = domain_create(config, mesh) + + ! 创建解 + solver%solution = solution_create(solver%domain) + + ! 保存原始时间步长 + solver%dt_original = config%dt + + ! 创建组件 + call solver%create_physics_components() + call solver%create_numerical_components() + + if (config%verbose) then + print *, "[PHYSICS SOLVER] Created:" + print *, " Mesh cells: ", mesh%ncells + print *, " Domain total cells: ", solver%domain%ntcells + end if + end function create_physics_solver + + ! ==================== 创建物理组件 ==================== + + subroutine create_physics_components(this) + class(physics_solver), intent(inout) :: this + + ! 检查是否启用物理模块 + if (.not. this%config%enable_physics) then + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Physics module disabled" + end if + return + end if + + ! 创建物理方程 + select case (trim(this%config%equation_type)) + case ("linear_advection") + allocate(linear_convection_eq :: this%equation) + select type(eq => this%equation) + type is(linear_convection_eq) + eq = create_linear_convection_eq(wave_speed=this%config%wave_speed) + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Created linear convection equation" + print *, " Wave speed: ", eq%wave_speed + end if + end select + + case default + if (this%config%verbose) then + print *, "[WARNING] Unknown equation type: ", trim(this%config%equation_type) + print *, " Using linear convection as default" + end if + + allocate(linear_convection_eq :: this%equation) + select type(eq => this%equation) + type is(linear_convection_eq) + eq = create_linear_convection_eq(wave_speed=this%config%wave_speed) + end select + end select + + ! 创建物理问题 + select case (trim(this%config%problem_type)) + case ("linear_advection") + allocate(linear_convection_prob :: this%problem) + select type(prob => this%problem) + type is(linear_convection_prob) + prob = create_linear_convection_prob( & + wave_speed=this%config%wave_speed, & + domain_length=this%config%domain_length, & + ic_type=this%config%ic_type, & + boundary_type=this%config%boundary_type) + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Created linear convection problem" + print *, " IC type: ", trim(prob%ic_type) + end if + end select + + case default + if (this%config%verbose) then + print *, "[WARNING] Unknown problem type: ", trim(this%config%problem_type) + print *, " Using linear convection as default" + end if + + allocate(linear_convection_prob :: this%problem) + select type(prob => this%problem) + type is(linear_convection_prob) + prob = create_linear_convection_prob( & + wave_speed=this%config%wave_speed, & + domain_length=this%config%domain_length, & + ic_type=this%config%ic_type, & + boundary_type=this%config%boundary_type) + end select + end select + + this%physics_initialized = .true. + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Physics components created" + end if + end subroutine create_physics_components + + ! ==================== 创建数值组件 ==================== + + subroutine create_numerical_components(this) + class(physics_solver), intent(inout) :: this + integer :: status + + ! 创建重构器 + this%reconstructor = create_reconstructor(this%config, status) + if (status /= 0) then + print *, "[ERROR] Failed to create reconstructor" + this%state = SOLVER_ERROR + this%error_message = "Failed to create reconstructor" + return + end if + + ! 创建通量计算器 + this%flux_calculator = create_flux_calculator(this%config, status) + if (status /= 0) then + print *, "[ERROR] Failed to create flux calculator" + this%state = SOLVER_ERROR + this%error_message = "Failed to create flux calculator" + return + end if + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Numerical components created" + end if + end subroutine create_numerical_components + + ! ==================== 初始化 ==================== + + subroutine physics_solver_initialize(this) + class(physics_solver), intent(inout) :: this + + if (this%state == SOLVER_INITIALIZED) then + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Already initialized" + end if + return + end if + + ! 如果启用了物理模块,应用初始条件 + if (this%physics_initialized .and. allocated(this%problem)) then + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Applying initial condition" + end if + + select type(prob => this%problem) + type is(linear_convection_prob) + ! 获取网格单元中心坐标 + call prob%initial_condition(this%mesh%xcc, & + this%solution%u(this%domain%ist:this%domain%ied-1)) + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Initial condition applied" + end if + end select + else + ! 简化的初始化:阶跃函数 + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Using simplified initialization" + end if + + ! 在 [0.5, 1.0] 区域内设为 2.0,其他区域为 1.0 + where (this%mesh%xcc >= 0.5_wp .and. this%mesh%xcc <= 1.0_wp) + this%solution%u(this%domain%ist:this%domain%ied-1) = 2.0_wp + elsewhere + this%solution%u(this%domain%ist:this%domain%ied-1) = 1.0_wp + end where + end if + + ! 同步旧场 + call this%solution%update_old_field() + + ! 更新状态 + this%state = SOLVER_INITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Initialized at t = ", this%current_time + end if + end subroutine physics_solver_initialize + + ! ==================== 时间步进 ==================== + + subroutine physics_solver_step(this, dt) + class(physics_solver), intent(inout) :: this + real(wp), intent(in) :: dt + + integer :: i + real(wp) :: u_val, f_val + + if (this%config%verbose .and. mod(this%current_step, 100) == 0) then + print *, "[PHYSICS SOLVER] Step ", this%current_step + 1, & + " dt = ", dt, " t = ", this%current_time + end if + + ! 更新旧场 + call this%solution%update_old_field() + + ! 简化的数值方法 + do i = this%domain%ist, this%domain%ied - 1 + u_val = this%solution%un(i) ! 使用旧值 + + ! 简单的线性对流:u_t + a*u_x = 0 + ! 使用一阶迎风格式 + this%solution%u(i) = u_val - dt * this%config%wave_speed * & + (u_val - this%solution%un(i-1)) / this%mesh%dx + end do + + ! 更新时间 + this%current_time = this%current_time + dt + this%current_step = this%current_step + 1 + + ! 每100步输出一次进度 + if (this%config%verbose .and. mod(this%current_step, 100) == 0) then + print *, "[PHYSICS SOLVER] Step ", this%current_step, & + " completed, t = ", this%current_time + end if + end subroutine physics_solver_step + + ! ==================== 运行到指定时间 ==================== + + subroutine physics_solver_run_to_time(this, final_time) + class(physics_solver), intent(inout) :: this + real(wp), intent(in) :: final_time + + real(wp) :: dt, t_remaining + integer :: step_count + + if (this%state /= SOLVER_INITIALIZED) then + this%error_message = "Solver not initialized" + this%state = SOLVER_ERROR + if (this%config%verbose) then + print *, "[PHYSICS SOLVER ERROR] Not initialized" + end if + return + end if + + this%state = SOLVER_RUNNING + step_count = 0 + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Running from t = ", this%current_time, & + " to t = ", final_time + end if + + do while (this%current_time < final_time - 1e-12_wp) + ! 计算时间步长 + t_remaining = final_time - this%current_time + dt = min(this%config%dt, t_remaining) + + ! 执行时间步 + call this%step(dt) + + step_count = step_count + 1 + + ! 每100步输出一次进度 + if (mod(step_count, 100) == 0 .and. this%config%verbose) then + print *, "[PHYSICS SOLVER] Progress: t = ", this%current_time, & + " / ", final_time + end if + end do + + ! 恢复原始时间步长 + this%config%dt = this%dt_original + + ! 更新状态 + this%state = SOLVER_COMPLETED + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Run completed:" + print *, " Final time: ", this%current_time + print *, " Total steps: ", this%current_step + print *, " Final u range: ", minval(this%solution%u), " to ", maxval(this%solution%u) + end if + end subroutine physics_solver_run_to_time + + ! ==================== 清理 ==================== + + subroutine physics_solver_cleanup(this) + class(physics_solver), intent(inout) :: this + + integer :: old_state + real(wp) :: old_time + integer(ip) :: old_step + + ! 保存清理前的状态用于调试 + old_state = this%state + old_time = this%current_time + old_step = this%current_step + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Cleaning up..." + print *, " Before cleanup - State: ", old_state + print *, " Before cleanup - Time: ", old_time + print *, " Before cleanup - Steps: ", old_step + end if + + ! 清理物理组件 + if (allocated(this%equation)) then + deallocate(this%equation) + if (this%config%verbose) then + print *, " Deallocated equation" + end if + end if + + if (allocated(this%problem)) then + deallocate(this%problem) + if (this%config%verbose) then + print *, " Deallocated problem" + end if + end if + + ! 清理数值组件 + if (allocated(this%reconstructor)) then + deallocate(this%reconstructor) + if (this%config%verbose) then + print *, " Deallocated reconstructor" + end if + end if + + if (allocated(this%flux_calculator)) then + deallocate(this%flux_calculator) + if (this%config%verbose) then + print *, " Deallocated flux calculator" + end if + end if + + ! 重置状态 - 但不重置解数组(保持分配) + this%state = SOLVER_UNINITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + this%error_message = "" + this%physics_initialized = .false. + + if (this%config%verbose) then + print *, " After cleanup - State: ", this%state + print *, " After cleanup - Time: ", this%current_time + print *, " After cleanup - Steps: ", this%current_step + print *, "[PHYSICS SOLVER] Cleanup completed" + end if + + end subroutine physics_solver_cleanup + + ! ==================== 状态查询 ==================== + + function physics_solver_get_state(this) result(state) + class(physics_solver), intent(in) :: this + integer :: state + state = this%state + end function physics_solver_get_state + + function physics_solver_get_error(this) result(error_msg) + class(physics_solver), intent(in) :: this + character(len=100) :: error_msg + error_msg = trim(this%error_message) + end function physics_solver_get_error + + ! ==================== 信息打印 ==================== + + subroutine physics_solver_print_info(this) + class(physics_solver), intent(in) :: this + + character(len=20) :: state_str + + ! 状态字符串 + select case (this%state) + case (SOLVER_UNINITIALIZED) + state_str = "Uninitialized" + case (SOLVER_INITIALIZED) + state_str = "Initialized" + case (SOLVER_RUNNING) + state_str = "Running" + case (SOLVER_COMPLETED) + state_str = "Completed" + case (SOLVER_ERROR) + state_str = "Error" + case default + write(state_str, '(A, I3)') "Unknown ", this%state + end select + + print *, "=== Physics Solver Information ===" + print *, "State: ", trim(state_str), " (", this%state, ")" + print *, "Current time: ", this%current_time + print *, "Current step: ", this%current_step + print *, "Error message: '", trim(this%error_message), "'" + + ! 配置信息 + print *, "Configuration:" + print *, " Scheme: ", trim(this%config%recon_scheme) + print *, " Order: ", this%config%spatial_order + print *, " dt: ", this%config%dt + print *, " Final time: ", this%config%final_time + + ! 域信息 + print *, "Domain:" + print *, " Ghost layers: ", this%domain%nghosts + print *, " Physical cells: ", this%domain%ist, " to ", this%domain%ied - 1 + + ! 物理信息 + print *, "Physics:" + print *, " Initialized: ", this%physics_initialized + print *, " Equation type: ", trim(this%config%equation_type) + print *, " Problem type: ", trim(this%config%problem_type) + + ! 解信息 + if (allocated(this%solution%u)) then + print *, "Solution:" + print *, " u size: ", size(this%solution%u) + print *, " u physical size: ", this%domain%ied - this%domain%ist + end if + + print *, "===================================" + end subroutine physics_solver_print_info + +end module physics_solver_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/test_physics_solver_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/test_physics_solver_simple.f90 new file mode 100644 index 00000000..ff659bac --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/test_physics_solver_simple.f90 @@ -0,0 +1,102 @@ +! tests/test_domain_solution.f90 +program test_domain_solution + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create, solution_reset + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + real(wp), allocatable :: initial_values(:) + integer :: i + + print *, "=== Domain and Solution Test ===" + print *, "" + + ! 测试1: 不同重构方案的ghost层计算 + print *, "1. Testing ghost layer calculation..." + print *, "--------------------------------------" + + ! ENO3 + call config_with_reconstruction(config, "eno", 3) + config%verbose = .false. + call mesh%init(ncells=10) + domain = domain_create(config, mesh) + print *, "ENO3: nghosts = ", domain%nghosts, " (expected: 3)" + + ! WENO3 + call config_with_reconstruction(config, "weno3", 3) + domain = domain_create(config, mesh) + print *, "WENO3: nghosts = ", domain%nghosts, " (expected: 2)" + + ! WENO5 + call config_with_reconstruction(config, "weno", 5) + domain = domain_create(config, mesh) + print *, "WENO5: nghosts = ", domain%nghosts, " (expected: 3)" + print *, "" + + ! 测试2: Solution数组 + print *, "2. Testing solution arrays..." + print *, "------------------------------" + + call config_with_reconstruction(config, "eno", 3) + config%verbose = .true. + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + domain = domain_create(config, mesh) + call domain%print_info() + print *, "" + + solution = solution_create(domain) + call solution%print_info() + print *, "" + + ! 测试3: 初始化和更新 + print *, "3. Testing initialization and update..." + print *, "----------------------------------------" + + allocate(initial_values(mesh%ncells)) + do i = 1, mesh%ncells + initial_values(i) = sin(2.0_wp * 3.14159265358979_wp * mesh%xcc(i) / mesh%L) + end do + + call solution%initialize(initial_values) + print *, "After initialization:" + print *, " u range: ", minval(solution%u), " to ", maxval(solution%u) + print *, " un range: ", minval(solution%un), " to ", maxval(solution%un) + + ! 修改当前解,测试更新 + solution%u = solution%u * 2.0_wp + call solution%update_old_field() + print *, "After update: max|u - un| = ", maxval(abs(solution%u - solution%un)) + print *, "" + + ! 测试4: 重置 + print *, "4. Testing reset..." + print *, "-------------------" + + call solution_reset(solution) + print *, "After reset:" + print *, " u max: ", maxval(abs(solution%u)) + print *, " un max: ", maxval(abs(solution%un)) + print *, " flux max: ", maxval(abs(solution%flux)) + print *, "" + + deallocate(initial_values) + + print *, "=== Test Summary ===" + print *, "✓ Ghost layer calculation works" + print *, "✓ Domain creation works" + print *, "✓ Solution arrays work" + print *, "✓ Initialization works" + print *, "✓ Field update works" + print *, "✓ Reset works" + print *, "" + print *, "Ready for next step: Implementing Physics modules" + +end program test_domain_solution \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/CMakeLists.txt new file mode 100644 index 00000000..b609e28d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/CMakeLists.txt @@ -0,0 +1,106 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +# 基础设施测试 +add_executable(test_infrastructure test_infrastructure.f90) +target_link_libraries(test_infrastructure + PRIVATE + infrastructure + core +) + +# 注册系统测试 +add_executable(test_registry test_registry.f90) +target_link_libraries(test_registry + PRIVATE + core + infrastructure +) + +# 物理模块测试 +add_executable(test_physics test_physics.f90) +target_link_libraries(test_physics + PRIVATE + physics + base +) + +# 组件管理器测试 +add_executable(test_component_manager test_component_manager.f90) +target_link_libraries(test_component_manager + PRIVATE + manager + infrastructure +) + +# 配置物理测试 +add_executable(test_config_physics test_config_physics.f90) +target_link_libraries(test_config_physics + PRIVATE + infrastructure + core +) + +# 求解器基础测试 +add_executable(test_solver_base test_solver_base.f90) +target_link_libraries(test_solver_base + PRIVATE + solver + infrastructure + core +) + +# 物理求解器测试 +add_executable(test_physics_solver test_physics_solver.f90) +target_link_libraries(test_physics_solver + PRIVATE + solver + infrastructure + core + physics + manager +) + +# 新增:简单物理求解器测试 +add_executable(test_physics_solver_simple test_physics_solver_simple.f90) +target_link_libraries(test_physics_solver_simple + PRIVATE + solver + infrastructure + core + physics + manager +) + +add_executable(test_simple_link test_simple_link.f90) +target_link_libraries(test_simple_link + PRIVATE + reconstructor + flux +) + + +add_executable(test_factory_simple test_factory_simple.f90) +target_link_libraries(test_factory_simple + PRIVATE + core + infrastructure + reconstructor + flux +) + +add_executable(test_domain_solution test_domain_solution.f90) +target_link_libraries(test_domain_solution + PRIVATE + infrastructure + core +) + +add_executable(test_component_manager_physics test_component_manager_physics.f90) +target_link_libraries(test_component_manager_physics + PRIVATE + manager + infrastructure + physics + core +) diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_cfd_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_cfd_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_cfd_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_component_manager.f90 new file mode 100644 index 00000000..f60c3505 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_component_manager.f90 @@ -0,0 +1,111 @@ +! tests/test_component_manager.f90 +program test_component_manager + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use component_manager_module, only: create_reconstructor, create_flux_calculator + use component_manager_module, only: component_manager_info, validate_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + implicit none + + type(cfd_config) :: config + class(reconstructor_base), allocatable :: recon + class(flux_calculator_base), allocatable :: flux + integer :: status + logical :: is_valid + + print *, "=== Component Manager Test ===" + print *, "" + + ! 显示组件管理器信息 + call component_manager_info() + print *, "" + + ! 测试1: 基本配置 + print *, "1. Testing basic ENO3 + Rusanov configuration..." + print *, "-----------------------------------------------" + + config%verbose = .true. + call config_print(config) + + ! 配置ENO3重构 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + call config_print(config) + print *, "" + + ! 验证配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Configuration is valid" + else + print *, "[ERROR] Configuration is invalid" + end if + print *, "" + + ! 测试2: 创建组件 + print *, "2. Testing component creation..." + print *, "--------------------------------" + + ! 创建重构器(带状态检查) + recon = create_reconstructor(config, status) + if (status == 0) then + print *, "[OK] Reconstructor created successfully" + call recon%info() + else + print *, "[ERROR] Failed to create reconstructor, code:", status + end if + print *, "" + + ! 创建通量计算器 + flux = create_flux_calculator(config, status) + if (status == 0) then + print *, "[OK] Flux calculator created successfully" + call flux%info() + else + print *, "[ERROR] Failed to create flux calculator, code:", status + end if + print *, "" + + ! 测试3: WENO3重构测试 + print *, "3. Testing WENO3 configuration..." + print *, "---------------------------------" + + call config_with_reconstruction(config, "weno3", 3) + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] WENO3 configuration is valid" + + ! 创建WENO3重构器 + recon = create_reconstructor(config) + call recon%info() + else + print *, "[ERROR] WENO3 configuration is invalid" + end if + print *, "" + + ! 测试4: 错误配置测试 + print *, "4. Testing invalid configuration..." + print *, "-----------------------------------" + + config%recon_scheme = "unknown_scheme" + config%flux_type = "unknown_flux" + + is_valid = validate_config(config) + if (.not. is_valid) then + print *, "[OK] Invalid configuration correctly rejected" + else + print *, "[ERROR] Invalid configuration should have been rejected" + end if + + ! 清理 + if (allocated(recon)) deallocate(recon) + if (allocated(flux)) deallocate(flux) + + print *, "" + print *, "=== Component manager test completed successfully ===" + +end program test_component_manager \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_component_manager_physics.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_component_manager_physics.f90 new file mode 100644 index 00000000..f2becca9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_component_manager_physics.f90 @@ -0,0 +1,120 @@ +! tests/test_component_manager_physics.f90 (简化版) +program test_component_manager_physics + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + use component_manager_module, only: component_manager_info, validate_config + + implicit none + + type(cfd_config) :: config + logical :: is_valid + + print *, "=== Component Manager Physics Test (Simplified) ===" + print *, "" + + ! 测试1: 显示组件管理器信息 + print *, "1. Testing component manager info..." + print *, "-------------------------------------" + call component_manager_info() + print *, "" + + ! 测试2: 物理模块测试(默认) + print *, "2. Testing physics module with default configuration..." + print *, "------------------------------------------------------" + + config%verbose = .true. + call config_print(config) + + ! 验证配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Default configuration is valid" + else + print *, "[ERROR] Default configuration is invalid" + end if + print *, "" + + ! 测试3: 测试物理配置 + print *, "3. Testing physics configuration..." + print *, "------------------------------------" + + ! 修改物理参数 + config%equation_type = "linear_advection" + config%problem_type = "linear_advection" + config%wave_speed = 2.5_wp + config%domain_length = 3.0_wp + + print *, "Modified physics configuration:" + print *, " Equation type: ", trim(config%equation_type) + print *, " Problem type: ", trim(config%problem_type) + print *, " Wave speed: ", config%wave_speed + print *, " Domain length: ", config%domain_length + print *, " Physics enabled: ", config%enable_physics + + ! 验证修改后的配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Modified physics configuration is valid" + else + print *, "[ERROR] Modified physics configuration is invalid" + end if + print *, "" + + ! 测试4: 数值组件测试 + print *, "4. Testing numerical components with physics..." + print *, "-----------------------------------------------" + + call config_with_reconstruction(config, "weno3", 3) + config%flux_type = "rusanov" + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Combined physics+numerics configuration is valid" + else + print *, "[ERROR] Combined configuration is invalid" + end if + print *, "" + + ! 测试5: 物理模块禁用测试 + print *, "5. Testing physics module disabled..." + print *, "---------------------------------------" + + config%enable_physics = .false. + config%verbose = .false. + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Configuration valid even with physics disabled" + else + print *, "[ERROR] Configuration should be valid with physics disabled" + end if + print *, "" + + ! 测试6: 错误配置测试 + print *, "6. Testing error handling..." + print *, "-----------------------------" + + config%verbose = .true. + config%enable_physics = .true. + config%recon_scheme = "unknown_scheme" + config%flux_type = "unknown_flux" + config%equation_type = "unknown_equation" + config%problem_type = "unknown_problem" + + is_valid = validate_config(config) + if (.not. is_valid) then + print *, "[OK] Invalid configuration correctly rejected" + else + print *, "[ERROR] Invalid configuration should have been rejected" + end if + print *, "" + + print *, "=== Component Manager Physics Test Summary ===" + print *, "✓ Component manager info works" + print *, "✓ Configuration validation works with physics" + print *, "✓ Error handling works correctly" + print *, "✓ Combined physics+numerics validation works" + print *, "" + print *, "下一步: 集成物理模块到求解器框架" + +end program test_component_manager_physics \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_config_physics.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_config_physics.f90 new file mode 100644 index 00000000..c6fef5c0 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_config_physics.f90 @@ -0,0 +1,141 @@ +! tests/test_config_physics.f90 (修复版) +program test_config_physics + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + + implicit none + + type(cfd_config) :: config + + print *, "=== Configuration Physics Test (Simplified) ===" + print *, "" + + ! 测试1: 默认配置 + print *, "1. Testing default configuration..." + print *, "-----------------------------------" + call config_print(config) + print *, "" + + ! 测试2: 验证基础物理字段 + print *, "2. Testing basic physics fields..." + print *, "----------------------------------" + + print *, "Verifying default physics fields:" + + if (trim(config%equation_type) == "linear_advection") then + print *, " ✓ Default equation type: linear_advection" + else + print *, " ✗ Unexpected equation type: ", trim(config%equation_type) + end if + + if (trim(config%problem_type) == "linear_advection") then + print *, " ✓ Default problem type: linear_advection" + else + print *, " ✗ Unexpected problem type: ", trim(config%problem_type) + end if + + if (abs(config%domain_length - 2.0_wp) < 1e-10_wp) then + print *, " ✓ Default domain length: 2.0" + else + print *, " ✗ Unexpected domain length: ", config%domain_length + end if + + if (config%enable_physics) then + print *, " ✓ Physics enabled by default" + else + print *, " ✗ Physics not enabled by default" + end if + + print *, "" + + ! 测试3: 使用类型绑定的方法(正确的方法名) + print *, "3. Testing type-bound procedures..." + print *, "--------------------------------------" + + call config%set_physics_parameters( & + equation_type="burgers_equation", & + problem_type="sod_shock_tube", & + domain_length=3.0_wp, & + enable_physics=.false.) + + print *, "After set_physics_parameters:" + print *, " Equation type: ", trim(config%equation_type) + print *, " Problem type: ", trim(config%problem_type) + print *, " Domain length: ", config%domain_length + print *, " Physics enabled: ", config%enable_physics + + if (trim(config%equation_type) == "burgers_equation") then + print *, " ✓ Equation type modified successfully via set_physics_parameters" + end if + + if (trim(config%problem_type) == "sod_shock_tube") then + print *, " ✓ Problem type modified successfully via set_physics_parameters" + end if + + if (abs(config%domain_length - 3.0_wp) < 1e-10_wp) then + print *, " ✓ Domain length modified successfully via set_physics_parameters" + end if + + if (.not. config%enable_physics) then + print *, " ✓ Physics disabled successfully via set_physics_parameters" + end if + + print *, "" + + ! 测试4: 调用get_physics_info方法 + print *, "4. Testing get_physics_info method..." + print *, "--------------------------------------" + call config%get_physics_info() + print *, "" + + ! 测试5: 高斯脉冲配置 + print *, "5. Testing Gaussian pulse configuration..." + print *, "-----------------------------------------" + + config%ic_type = "gaussian" + config%pulse_center = 0.6_wp + config%pulse_width = 0.15_wp + + print *, "Gaussian pulse parameters:" + print *, " IC type: ", trim(config%ic_type) + print *, " Center: ", config%pulse_center + print *, " Width: ", config%pulse_width + + if (trim(config%ic_type) == "gaussian") then + print *, " ✓ Gaussian IC type set" + end if + + if (abs(config%pulse_center - 0.6_wp) < 1e-10_wp) then + print *, " ✓ Pulse center set" + end if + + if (abs(config%pulse_width - 0.15_wp) < 1e-10_wp) then + print *, " ✓ Pulse width set" + end if + + print *, "" + + ! 测试6: 重构配置 + print *, "6. Testing reconstruction configuration..." + print *, "------------------------------------------" + + call config_with_reconstruction(config, "weno", 5) + + print *, "Reconstruction configuration:" + print *, " Scheme: ", trim(config%recon_scheme) + print *, " Order: ", config%spatial_order + + if (trim(config%recon_scheme) == "weno" .and. config%spatial_order == 5) then + print *, " ✓ WENO5 configuration successful" + else + print *, " ✗ Reconstruction configuration failed" + end if + + print *, "" + + print *, "=== Configuration Physics Test Complete ===" + print *, "✓ Config module updated with physics support" + print *, "✓ Fields can be directly accessed and modified" + print *, "✓ Type-bound procedures work correctly" + +end program test_config_physics \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_domain_solution.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_domain_solution.f90 new file mode 100644 index 00000000..ff659bac --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_domain_solution.f90 @@ -0,0 +1,102 @@ +! tests/test_domain_solution.f90 +program test_domain_solution + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create, solution_reset + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + real(wp), allocatable :: initial_values(:) + integer :: i + + print *, "=== Domain and Solution Test ===" + print *, "" + + ! 测试1: 不同重构方案的ghost层计算 + print *, "1. Testing ghost layer calculation..." + print *, "--------------------------------------" + + ! ENO3 + call config_with_reconstruction(config, "eno", 3) + config%verbose = .false. + call mesh%init(ncells=10) + domain = domain_create(config, mesh) + print *, "ENO3: nghosts = ", domain%nghosts, " (expected: 3)" + + ! WENO3 + call config_with_reconstruction(config, "weno3", 3) + domain = domain_create(config, mesh) + print *, "WENO3: nghosts = ", domain%nghosts, " (expected: 2)" + + ! WENO5 + call config_with_reconstruction(config, "weno", 5) + domain = domain_create(config, mesh) + print *, "WENO5: nghosts = ", domain%nghosts, " (expected: 3)" + print *, "" + + ! 测试2: Solution数组 + print *, "2. Testing solution arrays..." + print *, "------------------------------" + + call config_with_reconstruction(config, "eno", 3) + config%verbose = .true. + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + domain = domain_create(config, mesh) + call domain%print_info() + print *, "" + + solution = solution_create(domain) + call solution%print_info() + print *, "" + + ! 测试3: 初始化和更新 + print *, "3. Testing initialization and update..." + print *, "----------------------------------------" + + allocate(initial_values(mesh%ncells)) + do i = 1, mesh%ncells + initial_values(i) = sin(2.0_wp * 3.14159265358979_wp * mesh%xcc(i) / mesh%L) + end do + + call solution%initialize(initial_values) + print *, "After initialization:" + print *, " u range: ", minval(solution%u), " to ", maxval(solution%u) + print *, " un range: ", minval(solution%un), " to ", maxval(solution%un) + + ! 修改当前解,测试更新 + solution%u = solution%u * 2.0_wp + call solution%update_old_field() + print *, "After update: max|u - un| = ", maxval(abs(solution%u - solution%un)) + print *, "" + + ! 测试4: 重置 + print *, "4. Testing reset..." + print *, "-------------------" + + call solution_reset(solution) + print *, "After reset:" + print *, " u max: ", maxval(abs(solution%u)) + print *, " un max: ", maxval(abs(solution%un)) + print *, " flux max: ", maxval(abs(solution%flux)) + print *, "" + + deallocate(initial_values) + + print *, "=== Test Summary ===" + print *, "✓ Ghost layer calculation works" + print *, "✓ Domain creation works" + print *, "✓ Solution arrays work" + print *, "✓ Initialization works" + print *, "✓ Field update works" + print *, "✓ Reset works" + print *, "" + print *, "Ready for next step: Implementing Physics modules" + +end program test_domain_solution \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_factory_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_factory_simple.f90 new file mode 100644 index 00000000..db65da7c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_factory_simple.f90 @@ -0,0 +1,58 @@ +! tests/test_factory_simple.f90 (修复版) +program test_factory_simple + use base_modules, only: wp ! ← 添加这行 + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Basic systems + print *, "1. Testing basic systems..." + print *, "-----------------------------" + call config_print(config) + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Creating reconstructors + print *, "2. Testing reconstructors..." + print *, "------------------------------" + + ! 创建并测试ENO重构器 + print *, "Creating ENO reconstructor..." + eno = eno_reconstructor() ! 使用构造函数 + call eno%info() ! 必须调用info方法 + + print *, "" + print *, "Creating WENO3 reconstructor..." + weno3 = weno3_reconstructor() ! 使用构造函数 + call weno3%info() ! 必须调用info方法 + print *, "" + + ! Test 3: Creating flux calculator + print *, "3. Testing flux calculator..." + print *, "-------------------------------" + + print *, "Creating Rusanov flux calculator..." + rusanov = rusanov_flux() ! 使用构造函数 + call rusanov%info() ! 必须调用info方法 + print *, "" + + print *, "=== Factory pattern simple test completed successfully ===" + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_infrastructure.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_infrastructure.f90 new file mode 100644 index 00000000..22fa92d1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_infrastructure.f90 @@ -0,0 +1,56 @@ +! tests/test_infrastructure.f90 (原test_basic_only.f90) +program test_infrastructure + use base_modules, only: wp + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use registry_module, only: registry_init, registry_cleanup, & + register_component_simple, list_components + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "=== 基础设施测试 ===" + print *, "" + + ! 测试1: 配置 + print *, "1. 测试配置模块..." + print *, "-------------------" + call config_print(config) + print *, "" + + ! 测试2: 网格 + print *, "2. 测试网格模块..." + print *, "------------------" + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=5) + print *, "网格初始化:" + print *, " 单元数: ", mesh%ncells + print *, " 节点数: ", mesh%nnodes + print *, " 网格间距: ", mesh%dx + print *, "" + + ! 测试3: 注册系统 + print *, "3. 测试注册系统..." + print *, "------------------" + + call registry_init() + + ! 注册组件(使用简化版本) + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! 列出组件 + call list_components() + print *, "" + + ! 清理 + call registry_cleanup() + + print *, "=== 基础设施测试通过 ===" + print *, "✓ 配置模块工作正常" + print *, "✓ 网格模块工作正常" + print *, "✓ 注册系统工作正常" + +end program test_infrastructure \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_physics.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_physics.f90 new file mode 100644 index 00000000..3dababb6 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_physics.f90 @@ -0,0 +1,91 @@ +! tests/test_physics.f90 (原test_physics_minimal.f90) +program test_physics + use base_modules, only: wp, ip + use linear_convection_equation, only: linear_convection_eq, create_linear_convection_eq + use linear_convection_problem, only: linear_convection_prob, create_linear_convection_prob + + implicit none + + type(linear_convection_eq) :: eq + type(linear_convection_prob) :: prob + real(wp) :: u, f, a + real(wp), allocatable :: x(:), u_ic(:), u_exact(:) + integer :: i, nx = 10 + + print *, "=== 物理模块基础测试 ===" + print *, "" + + ! 测试1: 方程功能 + print *, "1. 测试方程功能..." + print *, "-------------------" + + eq = create_linear_convection_eq(wave_speed=2.0_wp) + print *, "方程: ", eq%name + print *, "波速: ", eq%wave_speed + + u = 1.5_wp + f = eq%flux(u) + a = eq%speed() + + print *, "u = ", u + print *, "F(u) = ", f, " (期望: 3.0)" + print *, "波速 a = ", a, " (期望: 2.0)" + + if (abs(f - 3.0_wp) < 1e-10_wp .and. abs(a - 2.0_wp) < 1e-10_wp) then + print *, "✓ 方程功能正常" + else + print *, "✗ 方程功能异常" + end if + print *, "" + + ! 测试2: 问题功能 + print *, "2. 测试问题功能..." + print *, "-------------------" + + prob = create_linear_convection_prob(ic_type="step", domain_length=2.0_wp) + print *, "问题: ", prob%name + print *, "初始条件类型: ", trim(prob%ic_type) + print *, "域长度: ", prob%domain_length + + allocate(x(nx), u_ic(nx), u_exact(nx)) + do i = 1, nx + x(i) = 0.0_wp + (i-1) * 0.2_wp + end do + + ! 测试初始条件 + call prob%initial_condition(x, u_ic) + print *, "初始条件范围: ", minval(u_ic), " 到 ", maxval(u_ic) + + ! 测试精确解 + u_exact = prob%exact_solution(x, 0.0_wp) + print *, "t=0时精确解范围: ", minval(u_exact), " 到 ", maxval(u_exact) + + ! 检查阶跃函数 + if (abs(u_ic(1) - 1.0_wp) < 1e-10_wp .and. & + abs(u_ic(6) - 2.0_wp) < 1e-10_wp) then + print *, "✓ 阶跃初始条件正确" + else + print *, "✗ 阶跃初始条件错误" + end if + + ! 检查精确解与初始条件一致 + if (maxval(abs(u_ic - u_exact)) < 1e-10_wp) then + print *, "✓ t=0时精确解与初始条件一致" + else + print *, "✗ 精确解计算错误" + end if + print *, "" + + ! 测试3: 边界条件接口 + print *, "3. 测试边界条件接口..." + print *, "----------------------" + call prob%boundary_condition(u_ic, 0.0_wp) + print *, "✓ 边界条件接口正常" + print *, "" + + deallocate(x, u_ic, u_exact) + + print *, "=== 物理模块测试完成 ===" + print *, "下一步: 将物理模块集成到现有系统中" + +end program test_physics \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_physics_solver.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_physics_solver.f90 new file mode 100644 index 00000000..ba49ffba --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_physics_solver.f90 @@ -0,0 +1,133 @@ +! tests/test_physics_solver.f90 (修复版) +program test_physics_solver + use base_modules, only: wp ! 使用一致的wp定义 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use mesh_module, only: mesh_type + use solver_base_module, only: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, & + SOLVER_COMPLETED, SOLVER_ERROR + use physics_solver_module, only: physics_solver + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(physics_solver) :: psolver + character(len=100) :: error_msg + integer :: state + + print *, "=== Physics Solver Test ===" + print *, "" + + ! 测试1: 创建物理求解器(默认物理配置) + print *, "1. Creating physics solver (default physics)..." + print *, "------------------------------------------------" + + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%dt = 0.01_wp + config%enable_physics = .true. + + call config_print(config) + + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + psolver = physics_solver(config, mesh) + call psolver%print_info() + print *, "" + + ! 测试2: 初始化 + print *, "2. Initializing physics solver..." + print *, "----------------------------------" + + call psolver%initialize() + state = psolver%get_state() + error_msg = psolver%get_error() + print *, "State after initialization: ", state + print *, "Expected: ", SOLVER_INITIALIZED + print *, "Match? ", state == SOLVER_INITIALIZED + print *, "Error message: '", trim(error_msg), "'" + print *, "" + + ! 测试3: 运行一小段时间 + print *, "3. Running physics solver (short time)..." + print *, "------------------------------------------" + + call psolver%run_to_time(0.02_wp) + state = psolver%get_state() + error_msg = psolver%get_error() + print *, "State after short run: ", state + print *, "Expected: ", SOLVER_COMPLETED + print *, "Match? ", state == SOLVER_COMPLETED + print *, "Current time: ", psolver%current_time + print *, "Current step: ", psolver%current_step + print *, "" + + ! 测试4: 禁用物理模块 + print *, "4. Testing physics solver with physics disabled..." + print *, "--------------------------------------------------" + + config%enable_physics = .false. + config%verbose = .false. + + psolver = physics_solver(config, mesh) + call psolver%initialize() + call psolver%run_to_time(0.01_wp) + + state = psolver%get_state() + error_msg = psolver%get_error() + print *, "State with physics disabled: ", state + print *, "Expected: ", SOLVER_COMPLETED + print *, "Match? ", state == SOLVER_COMPLETED + print *, "" + + ! 测试5: 不同物理配置 + print *, "5. Testing different physics configurations..." + print *, "----------------------------------------------" + + config%verbose = .true. + config%enable_physics = .true. + config%equation_type = "linear_advection" + config%problem_type = "linear_advection" + config%wave_speed = 2.5_wp + config%domain_length = 3.0_wp + config%ic_type = "gaussian" + + psolver = physics_solver(config, mesh) + call psolver%initialize() + + print *, "Physics configuration test completed" + print *, "" + + ! 测试6: 清理和错误处理 + print *, "6. Testing cleanup and error handling..." + print *, "----------------------------------------" + + call psolver%cleanup() + state = psolver%get_state() + error_msg = psolver%get_error() + print *, "State after cleanup: ", state + print *, "Expected: ", SOLVER_UNINITIALIZED + print *, "Match? ", state == SOLVER_UNINITIALIZED + + ! 尝试运行已清理的求解器 + call psolver%run_to_time(0.01_wp) + state = psolver%get_state() + error_msg = psolver%get_error() + print *, "State after error attempt: ", state + print *, "Expected: ", SOLVER_ERROR + print *, "Match? ", state == SOLVER_ERROR + print *, "Error message: '", trim(error_msg), "'" + print *, "" + + ! 最终信息 + print *, "=== Physics Solver Test Complete ===" + print *, "✓ Physics solver creation works" + print *, "✓ Physics component initialization works" + print *, "✓ Physics-enabled time stepping works" + print *, "✓ Physics disabled mode works" + print *, "✓ Different physics configurations work" + print *, "✓ Cleanup and error handling work" + print *, "" + print *, "下一步: 实现完整的数值方法(重构、通量、时间积分)" + +end program test_physics_solver \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_physics_solver_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_physics_solver_simple.f90 new file mode 100644 index 00000000..13312efd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_physics_solver_simple.f90 @@ -0,0 +1,161 @@ +! tests/test_physics_solver_simple.f90 +program test_physics_solver_simple + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction + use mesh_module, only: mesh_type + use physics_solver_module, only: physics_solver, SOLVER_COMPLETED + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(physics_solver) :: solver + real(wp) :: final_time, final_step + integer :: state + + print *, "=========================================" + print *, " 简单物理求解器测试" + print *, "=========================================" + print *, "" + + ! 步骤1: 配置 + print *, "[步骤1] 配置求解器..." + print *, "---------------------" + + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%dt = 0.01_wp + config%final_time = 0.1_wp + config%wave_speed = 1.0_wp + config%ic_type = "step" + config%boundary_type = "periodic" + config%equation_type = "linear_advection" + config%problem_type = "linear_advection" + config%enable_physics = .true. + config%domain_length = 1.0_wp + + print *, "配置参数:" + print *, " 重构格式: ", trim(config%recon_scheme) + print *, " 时间步长: ", config%dt + print *, " 最终时间: ", config%final_time + print *, " 波速: ", config%wave_speed + print *, "" + + ! 步骤2: 创建网格 + print *, "[步骤2] 创建网格..." + print *, "-------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + + print *, "网格信息:" + print *, " 单元数: ", mesh%ncells + print *, " 节点数: ", mesh%nnodes + print *, " 网格间距: ", mesh%dx + print *, "" + + ! 步骤3: 创建求解器 + print *, "[步骤3] 创建求解器..." + print *, "---------------------" + + solver = physics_solver(config, mesh) + + print *, "求解器创建成功" + print *, " 初始状态: ", solver%get_state() + print *, "" + + ! 步骤4: 初始化 + print *, "[步骤4] 初始化求解器..." + print *, "-----------------------" + + call solver%initialize() + + state = solver%get_state() + print *, "初始化完成" + print *, " 状态: ", state + print *, " 当前时间: ", solver%current_time + print *, " 当前步数: ", solver%current_step + print *, "" + + ! 步骤5: 运行求解器 + print *, "[步骤5] 运行求解器..." + print *, "---------------------" + + call solver%run_to_time(config%final_time) + + state = solver%get_state() + print *, "运行完成" + print *, " 状态: ", state + print *, " 最终时间: ", solver%current_time + print *, " 总步数: ", solver%current_step + print *, "" + + ! 步骤6: 保存结果 + print *, "[步骤6] 保存结果..." + print *, "-------------------" + + final_time = solver%current_time + final_step = real(solver%current_step, wp) + state = solver%get_state() + + print *, "保存的结果:" + print *, " 状态: ", state + print *, " 时间: ", final_time + print *, " 步数: ", final_step + print *, "" + + ! 步骤7: 清理求解器 + print *, "[步骤7] 清理求解器..." + print *, "---------------------" + + call solver%cleanup() + + print *, "清理后状态:" + print *, " 状态: ", solver%get_state() + print *, " 时间: ", solver%current_time + print *, " 步数: ", solver%current_step + print *, "" + + ! 步骤8: 验证结果 + print *, "[步骤8] 验证结果..." + print *, "-------------------" + + print *, "验证标准:" + print *, " 1. 运行后状态应为 COMPLETED (", SOLVER_COMPLETED, ")" + print *, " 2. 最终时间应接近 ", config%final_time + print *, " 3. 步数应大于 0" + print *, "" + + if (state == SOLVER_COMPLETED) then + print *, "✓ 状态验证通过: COMPLETED" + else + print *, "✗ 状态验证失败: 期望 ", SOLVER_COMPLETED, ", 实际 ", state + end if + + if (abs(final_time - config%final_time) < 1e-5_wp) then + print *, "✓ 时间验证通过: ", final_time, " ≈ ", config%final_time + else + print *, "✗ 时间验证失败: ", final_time, " ≠ ", config%final_time + end if + + if (final_step > 0) then + print *, "✓ 步数验证通过: ", final_step, " > 0" + else + print *, "✗ 步数验证失败: ", final_step, " ≤ 0" + end if + + print *, "" + + ! 最终判断 + if (state == SOLVER_COMPLETED .and. & + abs(final_time - config%final_time) < 1e-5_wp .and. & + final_step > 0) then + print *, "=========================================" + print *, " 所有测试通过! ✓" + print *, "=========================================" + else + print *, "=========================================" + print *, " 测试失败 ✗" + print *, "=========================================" + end if + +end program test_physics_solver_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_registry.f90 new file mode 100644 index 00000000..e82651ff --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_registry.f90 @@ -0,0 +1,87 @@ +! tests/test_registry.f90 (原test_minimal_simple.f90) +program test_registry + use base_modules, only: wp + use registry_module + use config_module + use mesh_module + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== 注册系统功能测试 ===" + print *, "" + + ! 测试1: 配置系统 + print *, "1. 测试配置系统" + print *, "--------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! 测试2: 网格系统 + print *, "2. 测试网格系统" + print *, "--------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! 测试3: 注册系统 + print *, "3. 测试注册系统" + print *, "--------------" + + call registry_init() + + ! 注册组件 + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call list_components() + print *, "注册表大小: ", registry_get_size() + print *, "" + + ! 测试组件查找 + print *, "4. 测试组件查找" + print *, "--------------" + + if (has_component_simple("reconstructor", "eno")) then + print *, "找到: reconstructor.eno" + else + print *, "未找到: reconstructor.eno" + end if + + if (has_component_simple("reconstructor", "unknown")) then + print *, "找到: reconstructor.unknown" + else + print *, "未找到: reconstructor.unknown" + end if + print *, "" + + ! 测试获取可用组件 + print *, "5. 测试注册系统功能" + print *, "------------------" + print *, "注册表已初始化: ", registry_is_initialized() + print *, "组件数量: ", registry_get_size() + print *, "" + + ! 清理 + call registry_cleanup() + + print *, "=== 注册系统测试完成 ===" + +end program test_registry \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_simple_link.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_simple_link.f90 new file mode 100644 index 00000000..71cc614e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_simple_link.f90 @@ -0,0 +1,78 @@ +! tests/test_simple_link.f90 +program test_simple_link + use base_modules, only: wp ! ← 添加这行 + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call registry_init() + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call list_components() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component_simple("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component_simple("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Cleanup + call registry_cleanup() + + print *, "=== Minimal test completed successfully ===" + +end program test_simple_link \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_solver_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_solver_base.f90 new file mode 100644 index 00000000..6cfe47e4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_solver_base.f90 @@ -0,0 +1,99 @@ +! tests/test_solver_base.f90 (修复版) +program test_solver_base + ! 所有 USE 语句必须在程序开始处 + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + use mesh_module, only: mesh_type + use solver_base_module, only: solver_base, SOLVER_UNINITIALIZED, & + SOLVER_INITIALIZED, SOLVER_COMPLETED, SOLVER_ERROR + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(solver_base) :: solver + integer :: state + + print *, "=== Solver Base Test ===" + print *, "" + + ! 测试1: 创建求解器 + print *, "1. Creating solver..." + print *, "----------------------" + + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%dt = 0.01_wp + + call config_print(config) + + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + solver = solver_base(config, mesh) + call solver%print_info() + print *, "" + + ! 测试2: 初始化 + print *, "2. Initializing solver..." + print *, "-------------------------" + + call solver%initialize() + state = solver%get_state() + print *, "State after initialization: ", state + print *, "Expected: ", SOLVER_INITIALIZED + print *, "Match? ", state == SOLVER_INITIALIZED + print *, "Error message: '", trim(solver%get_error()), "'" + print *, "" + + ! 测试3: 运行求解器 + print *, "3. Running solver..." + print *, "--------------------" + + call solver%run_to_time(0.05_wp) + state = solver%get_state() + print *, "State after run: ", state + print *, "Expected: ", SOLVER_COMPLETED + print *, "Match? ", state == SOLVER_COMPLETED + print *, "Current time: ", solver%current_time + print *, "Current step: ", solver%current_step + print *, "" + + ! 测试4: 再次运行(从已完成状态) + print *, "4. Running again from completed state..." + print *, "----------------------------------------" + + ! 需要先清理才能重新运行 + call solver%cleanup() + call solver%initialize() + call solver%run_to_time(0.1_wp) + + call solver%print_info() + print *, "" + + ! 测试5: 错误处理 + print *, "5. Testing error states..." + print *, "--------------------------" + + ! 创建一个未初始化的求解器 + call solver%cleanup() + state = solver%get_state() + print *, "Uninitialized state: ", state + print *, "Expected: ", SOLVER_UNINITIALIZED + print *, "Match? ", state == SOLVER_UNINITIALIZED + + ! 尝试运行未初始化的求解器 + call solver%run_to_time(0.01_wp) + state = solver%get_state() + print *, "State after error: ", state + print *, "Expected: ", SOLVER_ERROR + print *, "Match? ", state == SOLVER_ERROR + print *, "Error message: '", trim(solver%get_error()), "'" + print *, "" + + print *, "=== Solver Base Test Complete ===" + print *, "✓ Solver base class works" + print *, "✓ State management works" + print *, "✓ Time stepping framework works" + print *, "✓ Error handling works" + +end program test_solver_base \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_solver_framework.f90 b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_solver_framework.f90 new file mode 100644 index 00000000..6754323d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03c/tests/test_solver_framework.f90 @@ -0,0 +1,91 @@ +! tests/test_solver_framework.f90 +program test_solver_framework + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use mesh_module, only: mesh_type + use solver_module, only: cfd_solver, solver_create, solver_run, solver_cleanup + use solver_module, only: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, & + SOLVER_COMPLETED, SOLVER_ERROR + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(cfd_solver) :: solver + + print *, "=== 求解器框架测试 ===" + print *, "" + + ! 测试1: 基本创建 + print *, "1. 测试求解器创建..." + print *, "----------------------" + + ! 创建配置 + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.0_real64 + config%dt = 0.01_real64 + + call config_print(config) + print *, "" + + ! 创建网格 + call mesh%init(xmin=0.0_real64, xmax=2.0_real64, ncells=20) + call mesh%print_info() + print *, "" + + ! 创建求解器 + solver = solver_create(config, mesh) + print *, "✓ 求解器创建成功" + print *, " 状态: ", solver%get_state() + print *, "" + + ! 测试2: 求解器初始化 + print *, "2. 测试求解器初始化..." + print *, "------------------------" + + call solver%initialize() + print *, "✓ 求解器初始化完成" + print *, " 状态: ", solver%get_state() + print *, " 错误信息: '", trim(solver%get_error()), "'" + print *, "" + + ! 测试3: 简单运行 + print *, "3. 测试求解器运行..." + print *, "----------------------" + + call solver_run(solver, 0.05_real64) ! 运行到0.05秒 + print *, "✓ 求解器运行完成" + print *, " 最终状态: ", solver%get_state() + print *, "" + + ! 测试4: 清理 + print *, "4. 测试求解器清理..." + print *, "----------------------" + + call solver_cleanup(solver) + print *, "✓ 求解器清理完成" + print *, " 状态: ", solver%get_state() + print *, "" + + ! 测试5: 错误处理 + print *, "5. 测试错误处理..." + print *, "-------------------" + + ! 尝试重复初始化 + call solver%initialize() + print *, " 重复初始化状态: ", solver%get_state() + print *, " 错误信息: '", trim(solver%get_error()), "'" + + call solver_cleanup(solver) + print *, "" + + print *, "=== 框架测试总结 ===" + print *, "✓ 求解器创建/初始化/运行/清理流程验证完成" + print *, "✓ 状态管理正常工作" + print *, "✓ 错误处理机制就绪" + print *, "" + print *, "下一步: 添加实际数值计算功能" + +end program test_solver_framework \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03d/CMakeLists.txt new file mode 100644 index 00000000..55859dc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) +add_subdirectory(examples) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/README.md b/example/1d-linear-convection/weno3/fortran/registry/03d/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/examples/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03d/examples/CMakeLists.txt new file mode 100644 index 00000000..9207e0f4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/examples/CMakeLists.txt @@ -0,0 +1,22 @@ +# examples/CMakeLists.txt +message(STATUS "配置示例程序...") + +# 主示例程序:ENO/WENO对比 +add_executable(example_eno_weno_comparison + run_eno_weno.f90 +) + +target_link_libraries(example_eno_weno_comparison + PRIVATE + solver + infrastructure + core + physics + manager +) + +install(TARGETS example_eno_weno_comparison + RUNTIME DESTINATION bin/examples +) + +message(STATUS "示例程序配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/examples/run_eno_weno.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/examples/run_eno_weno.f90 new file mode 100644 index 00000000..a9c14d6a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/examples/run_eno_weno.f90 @@ -0,0 +1,282 @@ +! examples/run_eno_weno.f90 +program run_eno_weno + ! Example program: ENO/WENO comparison analysis + + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction, config_print + use mesh_module, only: mesh_type + use registry_module, only: registry_init, registry_cleanup, initialize_default_components + use physics_solver_module, only: physics_solver, SOLVER_COMPLETED + + implicit none + + type(cfd_config) :: config_eno3, config_weno3, config_weno5 + type(mesh_type) :: mesh + type(physics_solver) :: solver_eno3, solver_weno3, solver_weno5 + + ! Variables to save results + real(wp) :: time_eno3, time_weno3, time_weno5 + integer :: steps_eno3, steps_weno3, steps_weno5 + integer :: state_eno3, state_weno3, state_weno5 + logical :: all_success + + ! Debug: print start marker + print *, "==========================================" + print *, "START: ENO/WENO Comparison Analysis" + print *, "==========================================" + print *, "" + + ! Step 0: Initialize system + print *, "[STEP 0] Initializing system..." + print *, "--------------------------------" + + call registry_init(verbose=.true.) + print *, "Registry initialized" + + call initialize_default_components() + print *, "Default components registered" + print *, "" + + ! Step 1: Create mesh + print *, "[STEP 1] Creating computational mesh..." + print *, "----------------------------------------" + + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=40) + call mesh%print_info() + print *, "" + + ! ========== ENO3 Solver ========== + print *, "[STEP 2] Configuring and running ENO3 solver..." + print *, "-----------------------------------------------" + + ! Configure ENO3 + config_eno3%verbose = .true. + config_eno3%ic_type = "step" + config_eno3%wave_speed = 1.0_wp + config_eno3%final_time = 0.625_wp + config_eno3%dt = 0.0025_wp + config_eno3%rk_order = 2 + config_eno3%boundary_type = "periodic" + config_eno3%equation_type = "linear_advection" + config_eno3%problem_type = "linear_advection" + config_eno3%enable_physics = .true. + config_eno3%domain_length = 2.0_wp + + call config_with_reconstruction(config_eno3, "eno", 3) + + print *, "ENO3 configuration:" + call config_print(config_eno3) + print *, "" + + ! Create and run ENO3 solver + print *, "Creating ENO3 solver instance..." + solver_eno3 = physics_solver(config_eno3, mesh) + + print *, "Initializing ENO3 solver..." + call solver_eno3%initialize() + + print *, "Running ENO3 solver..." + call solver_eno3%run_to_time(config_eno3%final_time) + + ! Immediately save ENO3 results + time_eno3 = solver_eno3%current_time + steps_eno3 = solver_eno3%current_step + state_eno3 = solver_eno3%get_state() + + print *, "ENO3 solver completed" + print *, " Final time: ", time_eno3 + print *, " Total steps: ", steps_eno3 + print *, " State: ", state_eno3 + print *, "" + + ! ========== WENO3 Solver ========== + print *, "[STEP 3] Configuring and running WENO3 solver..." + print *, "------------------------------------------------" + + ! Configure WENO3 + config_weno3%verbose = .true. + config_weno3%ic_type = "step" + config_weno3%wave_speed = 1.0_wp + config_weno3%final_time = 0.625_wp + config_weno3%dt = 0.0025_wp + config_weno3%rk_order = 2 + config_weno3%boundary_type = "periodic" + config_weno3%equation_type = "linear_advection" + config_weno3%problem_type = "linear_advection" + config_weno3%enable_physics = .true. + config_weno3%domain_length = 2.0_wp + + call config_with_reconstruction(config_weno3, "weno3", 3) + + print *, "WENO3 configuration:" + call config_print(config_weno3) + print *, "" + + ! Create and run WENO3 solver + print *, "Creating WENO3 solver instance..." + solver_weno3 = physics_solver(config_weno3, mesh) + + print *, "Initializing WENO3 solver..." + call solver_weno3%initialize() + + print *, "Running WENO3 solver..." + call solver_weno3%run_to_time(config_weno3%final_time) + + ! Immediately save WENO3 results + time_weno3 = solver_weno3%current_time + steps_weno3 = solver_weno3%current_step + state_weno3 = solver_weno3%get_state() + + print *, "WENO3 solver completed" + print *, " Final time: ", time_weno3 + print *, " Total steps: ", steps_weno3 + print *, " State: ", state_weno3 + print *, "" + + ! ========== WENO5 Solver ========== + print *, "[STEP 4] Configuring and running WENO5 solver..." + print *, "------------------------------------------------" + + ! Configure WENO5 + config_weno5%verbose = .true. + config_weno5%ic_type = "step" + config_weno5%wave_speed = 1.0_wp + config_weno5%final_time = 0.625_wp + config_weno5%dt = 0.0025_wp + config_weno5%rk_order = 2 + config_weno5%boundary_type = "periodic" + config_weno5%equation_type = "linear_advection" + config_weno5%problem_type = "linear_advection" + config_weno5%enable_physics = .true. + config_weno5%domain_length = 2.0_wp + + call config_with_reconstruction(config_weno5, "weno", 5) + + print *, "WENO5 configuration:" + call config_print(config_weno5) + print *, "" + + ! Create and run WENO5 solver + print *, "Creating WENO5 solver instance..." + solver_weno5 = physics_solver(config_weno5, mesh) + + print *, "Initializing WENO5 solver..." + call solver_weno5%initialize() + + print *, "Running WENO5 solver..." + call solver_weno5%run_to_time(config_weno5%final_time) + + ! Immediately save WENO5 results + time_weno5 = solver_weno5%current_time + steps_weno5 = solver_weno5%current_step + state_weno5 = solver_weno5%get_state() + + print *, "WENO5 solver completed" + print *, " Final time: ", time_weno5 + print *, " Total steps: ", steps_weno5 + print *, " State: ", state_weno5 + print *, "" + + ! ========== Results Summary (BEFORE cleanup!) ========== + print *, "==========================================" + print *, " RESULTS SUMMARY" + print *, "==========================================" + print *, "" + + print *, "Solver Performance Comparison:" + print *, "------------------------------" + + print *, "ENO3:" + print *, " Final time: ", time_eno3 + print *, " Total steps: ", steps_eno3 + print *, " State: ", state_eno3 + print *, "" + + print *, "WENO3:" + print *, " Final time: ", time_weno3 + print *, " Total steps: ", steps_weno3 + print *, " State: ", state_weno3 + print *, "" + + print *, "WENO5:" + print *, " Final time: ", time_weno5 + print *, " Total steps: ", steps_weno5 + print *, " State: ", state_weno5 + print *, "" + + ! ========== Final Judgment ========== + print *, "==========================================" + print *, " FINAL JUDGMENT" + print *, "==========================================" + print *, "" + + all_success = (state_eno3 == SOLVER_COMPLETED) .and. & + (state_weno3 == SOLVER_COMPLETED) .and. & + (state_weno5 == SOLVER_COMPLETED) + + if (all_success) then + print *, "✓ ALL SOLVERS SUCCESSFULLY COMPLETED!" + print *, "" + print *, "Parameter Summary:" + print *, " Grid cells: ", mesh%ncells + print *, " Time step: ", config_eno3%dt + print *, " Final time: ", config_eno3%final_time + print *, " Wave speed: ", config_eno3%wave_speed + print *, " IC type: ", trim(config_eno3%ic_type) + print *, "" + print *, "Performance Comparison:" + print *, " ENO3: ", steps_eno3, " steps" + print *, " WENO3: ", steps_weno3, " steps" + print *, " WENO5: ", steps_weno5, " steps" + else + print *, "✗ SOME SOLVERS FAILED" + print *, "" + print *, "Failure Analysis:" + if (state_eno3 /= SOLVER_COMPLETED) then + print *, " • ENO3 failed with state: ", state_eno3 + end if + if (state_weno3 /= SOLVER_COMPLETED) then + print *, " • WENO3 failed with state: ", state_weno3 + end if + if (state_weno5 /= SOLVER_COMPLETED) then + print *, " • WENO5 failed with state: ", state_weno5 + end if + end if + + print *, "" + print *, "==========================================" + print *, " ANALYSIS COMPLETE" + print *, "==========================================" + + ! ========== Cleanup (AFTER printing results!) ========== + print *, "" + print *, "[STEP 5] Cleaning up system..." + print *, "--------------------------------" + + call solver_eno3%cleanup() + call solver_weno3%cleanup() + call solver_weno5%cleanup() + call registry_cleanup() + + print *, "All solvers cleaned up" + print *, "Registry cleaned up" + print *, "" + + ! ========== Wait for user input before exit ========== + print *, "==========================================" + print *, "Press ENTER to exit..." + print *, "==========================================" + + ! Wait for user input (uncomment if needed) + ! read(*,*) + + ! Alternative: add a small delay + print *, "Program will exit in 3 seconds..." + call sleep(3) + + print *, "" + print *, "==========================================" + print *, " PROGRAM END" + print *, "==========================================" + +end program run_eno_weno \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/build.bat new file mode 100644 index 00000000..6fd6dc03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/build.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Project Builder +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python build script with full Intel environment support... +echo. + +python build.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Build failed + pause + exit /b 1 +) + +echo. +echo [INFO] Build completed successfully! +echo. +echo [INFO] To run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/build.py new file mode 100644 index 00000000..3bf6d537 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/build.py @@ -0,0 +1,629 @@ +#!/usr/bin/env python3 +""" +Fortran CFD Project Builder - 完整Python解决方案 +在Python内部处理Intel oneAPI环境配置 +""" + +import os +import sys +import subprocess +import shutil +import argparse +import time +import platform +import tempfile +from pathlib import Path + +class IntelEnvironment: + """Intel oneAPI环境管理器""" + + def __init__(self): + self.setvars_path = None + self.env_vars = {} + + def find_setvars(self): + """查找setvars.bat文件""" + possible_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + r"C:\Program Files (x86)\Intel\oneAPI\compiler\latest\env\vars.bat", + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\setvars.bat"), + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\compiler\latest\env\vars.bat"), + ] + + for path in possible_paths: + if os.path.exists(path): + self.setvars_path = path + return True + + return False + + def setup_environment(self): + """设置Intel环境""" + if not self.find_setvars(): + return False + + try: + # 创建临时的批处理文件来捕获环境变量 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + f.write(f'@echo off\n') + f.write(f'call "{self.setvars_path}" >nul 2>&1\n') + f.write(f'set\n') # 输出所有环境变量 + temp_bat = f.name + + # 运行批处理文件并捕获输出 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + shell=True + ) + + # 解析环境变量 + for line in result.stdout.split('\n'): + line = line.strip() + if '=' in line: + key, value = line.split('=', 1) + self.env_vars[key.strip()] = value.strip() + + # 清理临时文件 + os.unlink(temp_bat) + + # 更新当前进程的环境变量 + os.environ.update(self.env_vars) + + return True + + except Exception as e: + print(f"设置Intel环境失败: {e}") + return False + + def get_compiler_info(self): + """获取编译器信息""" + info = {} + + # 检查ifx编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + env={**os.environ, **self.env_vars} if self.env_vars else os.environ + ) + + if result.returncode == 0: + for line in result.stdout.split('\n'): + if 'Version' in line or '版本' in line: + info['ifx_version'] = line.strip() + break + except: + pass + + # 检查环境变量 + info['ifx_root'] = self.env_vars.get('IFX_ROOT', '') + info['compiler_root'] = self.env_vars.get('ONEAPI_ROOT', '') + + return info + +class BuildSystem: + """构建系统主类""" + + def __init__(self): + self.project_root = Path(__file__).parent.parent + self.build_dir = self.project_root / "build" + self.intel_env = IntelEnvironment() + + # 设置控制台编码 + if sys.platform == "win32": + try: + import ctypes + # 设置控制台输出为UTF-8 + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + except: + pass + + def print_header(self, text): + """打印标题""" + print(f"\n{'='*70}") + print(f" {text}") + print(f"{'='*70}\n") + + def print_step(self, step, total, message): + """打印步骤""" + print(f"[{step}/{total}] {message}...") + + def print_success(self, message): + """打印成功""" + print(f"\033[92m✓ {message}\033[0m") + + def print_error(self, message): + """打印错误""" + print(f"\033[91m✗ {message}\033[0m") + + def print_warning(self, message): + """打印警告""" + print(f"\033[93m! {message}\033[0m") + + def print_info(self, message): + """打印信息""" + print(f"\033[94mℹ {message}\033[0m") + + def check_prerequisites(self): + """检查前提条件""" + self.print_step(1, 6, "检查前提条件") + + # 检查Python版本 + python_version = sys.version.split()[0] + self.print_info(f"Python版本: {python_version}") + + # 检查平台 + self.print_info(f"平台: {platform.system()} {platform.release()}") + self.print_info(f"处理器核心数: {os.cpu_count()}") + + # 检查CMake + try: + result = subprocess.run( + ["cmake", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + version_line = result.stdout.split('\n')[0] + self.print_success(f"CMake: {version_line}") + else: + self.print_error("CMake未找到") + return False + except FileNotFoundError: + self.print_error("CMake未安装") + return False + + return True + + def setup_intel_environment(self, args): + """设置Intel环境""" + self.print_step(2, 6, "配置Intel oneAPI环境") + + if not self.intel_env.find_setvars(): + self.print_warning("未找到Intel oneAPI setvars.bat") + self.print_info("将尝试使用系统环境中的编译器") + + # 检查是否能直接访问编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + self.print_success("Intel编译器在系统PATH中找到") + return True + else: + self.print_warning("Intel编译器未在PATH中找到") + except: + self.print_warning("无法访问Intel编译器") + + return True # 继续,让CMake自己找编译器 + + # 设置环境 + if self.intel_env.setup_environment(): + compiler_info = self.intel_env.get_compiler_info() + + if compiler_info.get('ifx_version'): + self.print_success(f"Intel Fortran编译器: {compiler_info['ifx_version']}") + elif compiler_info.get('ifx_root'): + self.print_success(f"Intel编译器路径: {compiler_info['ifx_root']}") + else: + self.print_success("Intel oneAPI环境配置完成") + + return True + else: + self.print_warning("Intel环境配置失败,将继续使用系统环境") + return True + + def clean_build_directory(self, args): + """清理构建目录""" + if args.clean and self.build_dir.exists(): + self.print_info("清理构建目录...") + try: + shutil.rmtree(self.build_dir) + self.print_success("构建目录已清理") + except Exception as e: + self.print_error(f"清理失败: {e}") + if not args.force: + return False + return True + + def run_command(self, cmd, cwd=None, check=True, env=None): + """运行命令""" + if isinstance(cmd, list): + cmd_str = ' '.join(str(c) for c in cmd if c) + else: + cmd_str = str(cmd) + + print(f" \033[96m$\033[0m {cmd_str}") + + try: + # 合并环境变量 + exec_env = os.environ.copy() + if env: + exec_env.update(env) + if self.intel_env.env_vars: + exec_env.update(self.intel_env.env_vars) + + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=False, + env=exec_env + ) + + # 处理输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or '完成' in line or '生成' in line: + print(f" \033[92m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + if check and result.returncode != 0: + self.print_error(f"命令执行失败,退出码: {result.returncode}") + return False + + return True + + except Exception as e: + self.print_error(f"命令执行异常: {e}") + return False + + def configure_cmake(self, args): + """配置CMake""" + self.print_step(3, 6, "配置CMake项目") + + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ] + + if args.compiler == "ifx": + cmake_cmd.extend(["-T", "fortran=ifx"]) + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + success = self.run_command(cmake_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("CMake配置完成") + else: + self.print_error("CMake配置失败") + + return success + + def build_project(self, args): + """构建项目""" + self.print_step(4, 6, "构建项目") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + success = self.run_command(build_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("项目构建完成") + else: + self.print_error("构建失败") + + return success + + def run_tests_with_environment(self, test_exe): + """运行单个测试,确保有Intel环境""" + try: + # 创建临时的批处理文件来运行测试 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'"{test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'"{test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + return result + + except Exception as e: + print(f"运行测试失败: {e}") + return None + + def run_tests(self, args): + """运行测试""" + self.print_step(5, 6, "运行测试") + + # 查找测试可执行文件 + test_dir = self.build_dir / "bin" / args.build_type + if not test_dir.exists(): + test_dir = self.build_dir / "bin" + if not test_dir.exists(): + test_dir = self.build_dir + + test_files = list(test_dir.glob("test_*.exe")) + + if not test_files: + self.print_warning("未找到测试程序") + return True + + all_passed = True + + for test_exe in sorted(test_files): + test_name = test_exe.stem + self.print_info(f"运行测试: {test_name}") + print(f" {'-'*50}") + + # 运行测试 + result = self.run_tests_with_environment(str(test_exe)) + + if result is None: + self.print_error(f" {test_name} 运行失败") + all_passed = False + continue + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + print(f" {line}") + + if result.returncode == 0: + self.print_success(f" {test_name} 通过") + else: + self.print_error(f" {test_name} 失败 (退出码: {result.returncode})") + all_passed = False + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + print() # 空行 + + return all_passed + + def create_test_runner(self, args): + """创建独立的测试运行器""" + self.print_step(6, 6, "创建测试运行器") + + runner_path = self.build_dir / "run_tests.bat" + + content = f'''@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Test Runner +echo ======================================== +echo. + +REM Setup Intel oneAPI environment +set "SETVARS_PATH={self.intel_env.setvars_path or ''}" +if exist "%SETVARS_PATH%" ( + call "%SETVARS_PATH%" >nul + echo [INFO] Intel environment configured +) else ( + echo [WARNING] Intel environment not found + echo [WARNING] Tests may fail without runtime libraries +) + +echo. + +REM Run all test executables +set "TEST_COUNT=0" +set "PASS_COUNT=0" + +for %%f in ("bin\\{args.build_type}\\test_*.exe") do ( + set /a TEST_COUNT+=1 + echo [TEST %%f] + echo {'-'*50} + + %%f + if errorlevel 1 ( + echo [FAILED] %%f + ) else ( + echo [PASSED] %%f + set /a PASS_COUNT+=1 + ) + echo. +) + +echo ======================================== +echo Tests: %PASS_COUNT%/%TEST_COUNT% passed +if %PASS_COUNT% equ %TEST_COUNT% ( + echo [SUCCESS] All tests passed! +) else ( + echo [FAILURE] Some tests failed +) +echo ======================================== + +pause +''' + + with open(runner_path, 'w', encoding='utf-8') as f: + f.write(content) + + self.print_success(f"测试运行器已创建: {runner_path}") + self.print_info(f"使用方法: cd build && run_tests.bat") + + return runner_path + + def generate_report(self, args, build_time, tests_passed): + """生成构建报告""" + self.print_header("构建完成") + + print(f"项目: {self.project_root.name}") + print(f"构建类型: {args.build_type}") + print(f"编译器: {args.compiler}") + print(f"并行作业: {args.jobs}") + print(f"总耗时: {build_time:.1f}秒") + print(f"测试结果: {'全部通过' if tests_passed else '有失败'}") + + # 显示生成的可执行文件 + bin_dir = self.build_dir / "bin" / args.build_type + if bin_dir.exists(): + print(f"\n生成的可执行文件:") + for exe in sorted(bin_dir.glob("*.exe")): + size_mb = exe.stat().st_size / (1024 * 1024) + print(f" • {exe.name} ({size_mb:.2f} MB)") + + # 显示测试运行器信息 + runner_path = self.build_dir / "run_tests.bat" + if runner_path.exists(): + print(f"\n独立测试运行器:") + print(f" • {runner_path.name}") + print(f" 在Intel oneAPI环境中运行所有测试") + + def run(self): + """运行构建系统""" + parser = argparse.ArgumentParser( + description="Fortran CFD项目构建工具", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认构建 + %(prog)s --clean # 清理后构建 + %(prog)s --build-type Release # Release构建 + %(prog)s --no-tests # 只构建,不运行测试 + %(prog)s -j8 --verbose # 8线程并行构建,详细输出 + """ + ) + + parser.add_argument("--build-type", choices=["Debug", "Release"], + default="Debug", help="构建类型") + parser.add_argument("--compiler", choices=["ifx", "ifort"], + default="ifx", help="Fortran编译器") + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-tests", action="store_true", + help="跳过测试") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + + args = parser.parse_args() + + # 开始构建 + start_time = time.time() + + self.print_header("Fortran CFD 项目构建系统") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 检查前提条件 + if not self.check_prerequisites(): + if not args.force: + return 1 + + # 2. 设置Intel环境 + if not self.setup_intel_environment(args): + if not args.force: + return 1 + + # 3. 清理目录 + if not self.clean_build_directory(args): + if not args.force: + return 1 + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 4. 配置CMake + if not self.configure_cmake(args): + if not args.force: + return 1 + + # 5. 构建项目 + if not self.build_project(args): + if not args.force: + return 1 + + # 6. 运行测试和创建测试运行器 + tests_passed = True + if not args.no_tests: + tests_passed = self.run_tests(args) + + # 创建测试运行器 + self.create_test_runner(args) # 传递 args 参数 + + # 7. 生成报告 + build_time = time.time() - start_time + self.generate_report(args, build_time, tests_passed) + + return 0 if tests_passed else 1 + + except KeyboardInterrupt: + self.print_error("\n构建被用户中断") + return 1 + except Exception as e: + self.print_error(f"构建过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + builder = BuildSystem() + return builder.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/run_all_steps.bat b/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/run_all_steps.bat new file mode 100644 index 00000000..d506149b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/run_all_steps.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo CFD Project: All Steps +echo ======================================== +echo. + +echo [INFO] Starting Step 1: Physics Modules Test... +call run_step1.bat + +if errorlevel 1 ( + echo [ERROR] Step 1 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Starting Step 2: Configuration Physics Update... +call run_step2.bat + +if errorlevel 1 ( + echo [ERROR] Step 2 failed + pause + exit /b 1 +) + +echo. +echo ======================================== +echo All Steps Completed Successfully! +echo ======================================== +echo. +echo [INFO] Next: Update component manager for physics support +echo [INFO] Run: run_step3.bat (to be created) +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/run_example.py b/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/run_example.py new file mode 100644 index 00000000..d7c19917 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/run_example.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +# scripts/run_example.py +""" +运行ENO/WENO示例程序 +""" + +import os +import sys +import subprocess +from pathlib import Path + +# 添加当前目录到路径 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import BuildSystem +except ImportError: + print("错误: 找不到build.py,请确保在scripts目录中运行") + sys.exit(1) + +def run_example(): + """运行示例程序""" + builder = BuildSystem() + + print("\n" + "="*70) + print(" 运行ENO/WENO对比示例程序") + print("="*70 + "\n") + + # 检查是否已构建 + exe_path = builder.build_dir / "bin" / "Debug" / "example_eno_weno_comparison.exe" + + if not exe_path.exists(): + print("示例程序未构建,先构建项目...") + print("-"*50) + + # 使用简化的构建 + result = subprocess.run( + ["python", "build.py", "--no-tests", "--clean"], + cwd=builder.project_root / "scripts", + capture_output=True, + text=True + ) + + if result.returncode != 0: + print("构建失败:") + print(result.stderr) + return False + + # 运行示例程序 + print("运行示例程序...") + print("-"*50) + + try: + result = subprocess.run( + [str(exe_path)], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace' + ) + + print(result.stdout) + + if result.stderr: + print("标准错误输出:") + print(result.stderr) + + return result.returncode == 0 + + except Exception as e: + print(f"运行示例程序失败: {e}") + return False + +def main(): + """主函数""" + success = run_example() + + if success: + print("\n" + "="*70) + print(" ✓ 示例程序运行成功") + print("="*70) + return 0 + else: + print("\n" + "="*70) + print(" ✗ 示例程序运行失败") + print("="*70) + return 1 + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/run_step1.bat b/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/run_step1.bat new file mode 100644 index 00000000..0b6b1f17 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/run_step1.bat @@ -0,0 +1,39 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Step 1: Physics Modules Test +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python step1 script with full Intel environment support... +echo. + +python run_step1.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Step 1 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Step 1 completed successfully! +echo. +echo [INFO] Next step: Update config to include physics settings +echo [INFO] Run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/run_step1.py b/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/run_step1.py new file mode 100644 index 00000000..5e087a69 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/run_step1.py @@ -0,0 +1,319 @@ +#!/usr/bin/env python3 +""" +Step 1: Physics Modules Test +扩展build.py,专门用于测试物理模块 +""" + +import os +import sys +import subprocess +import time +from pathlib import Path + +# 添加当前目录到路径,以便导入build.py的类 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import IntelEnvironment, BuildSystem +except ImportError: + print("Error: Cannot import build.py. Make sure build.py is in the same directory.") + sys.exit(1) + +class Step1System(BuildSystem): + """Step 1 测试系统,继承自BuildSystem""" + + def __init__(self): + super().__init__() + self.test_name = "test_physics_minimal" + self.test_exe = None + + def find_test_executable(self): + """查找测试可执行文件""" + possible_paths = [ + self.build_dir / "bin" / "Debug" / f"{self.test_name}.exe", + self.build_dir / "Debug" / f"{self.test_name}.exe", + self.build_dir / f"{self.test_name}.exe", + self.build_dir / "bin" / f"{self.test_name}.exe", + ] + + for path in possible_paths: + if path.exists(): + self.test_exe = path + self.print_success(f"Found test executable: {path}") + return True + + # 如果没有找到,尝试搜索 + self.print_warning(f"Could not find {self.test_name}.exe") + self.print_info("Searching for test executables...") + + try: + result = subprocess.run( + ["dir", str(self.build_dir), "/s", "/b", "*.exe"], + capture_output=True, + text=True, + encoding='utf-8', + shell=True + ) + + if result.returncode == 0: + test_files = [line.strip() for line in result.stdout.split('\n') + if line and 'test_' in line.lower()] + + if test_files: + self.print_info("Found test files:") + for test_file in test_files: + self.print_info(f" {test_file}") + return False + except: + pass + + return False + + def run_test_with_intel_env(self): + """在Intel环境下运行测试""" + if not self.test_exe: + self.print_error("No test executable found") + return False + + self.print_step(1, 2, f"Running test: {self.test_exe.name}") + + try: + # 创建临时的批处理文件来运行测试(包含Intel环境) + import tempfile + + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + # 设置Intel环境 + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'echo [INFO] Intel environment configured\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'echo [WARNING] Intel environment not found\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + self.print_info(f"Command: {temp_bat}") + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower() or 'fail' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or 'pass' in line.lower() or '✓' in line: + print(f" \033[92m{line}\033[0m") + elif '=' in line or '---' in line: + print(f" \033[96m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + self.print_warning("Test stderr output:") + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + self.print_step(2, 2, "Test execution completed") + + if result.returncode == 0: + self.print_success("Test passed") + return True + else: + self.print_error(f"Test failed (exit code: {result.returncode})") + return False + + except Exception as e: + self.print_error(f"Failed to run test: {e}") + return False + + def build_project_if_needed(self, args): + """如果需要,构建项目""" + if args.no_build: + self.print_info("Skipping build (--no-build flag)") + return True + + self.print_step(1, 3, "Building project") + + # 调用父类的构建方法 + build_args = argparse.Namespace() + build_args.clean = args.clean + build_args.build_type = "Debug" + build_args.compiler = "ifx" + build_args.no_tests = True # 不运行所有测试 + build_args.jobs = os.cpu_count() + build_args.verbose = args.verbose + build_args.force = args.force + + # 清理构建目录 + if args.clean and self.build_dir.exists(): + self.print_info("Cleaning build directory...") + import shutil + try: + shutil.rmtree(self.build_dir) + self.print_success("Build directory cleaned") + except Exception as e: + self.print_error(f"Clean failed: {e}") + if not args.force: + return False + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 配置CMake + self.print_step(2, 3, "Configuring CMake") + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-DCMAKE_BUILD_TYPE=Debug", + "-T", "fortran=ifx", + ] + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + if not self.run_command(cmake_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + # 构建项目 + self.print_step(3, 3, "Building project") + build_cmd = [ + "cmake", + "--build", ".", + "--config", "Debug", + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + if not self.run_command(build_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + self.print_success("Build completed") + return True + + def run(self): + """运行Step 1测试""" + parser = argparse.ArgumentParser( + description="Step 1: Physics Modules Test", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认运行 + %(prog)s --clean # 清理后构建并测试 + %(prog)s --no-build # 只运行测试,不重新构建 + %(prog)s --verbose # 详细输出 + %(prog)s -j4 # 使用4个并行作业构建 + """ + ) + + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-build", action="store_true", + help="不重新构建,直接运行测试") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + + args = parser.parse_args() + + # 开始测试 + start_time = time.time() + + self.print_header("Step 1: Physics Modules Implementation Test") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 设置Intel环境 + self.print_step(1, 4, "Setting up Intel oneAPI environment") + if not self.setup_intel_environment(args): + if not args.force: + self.print_error("Intel environment setup failed") + return 1 + self.print_warning("Intel environment setup failed, continuing...") + + # 2. 构建项目(如果需要) + self.print_step(2, 4, "Building project if needed") + if not self.build_project_if_needed(args): + if not args.force: + return 1 + + # 3. 查找测试可执行文件 + self.print_step(3, 4, "Finding test executable") + if not self.find_test_executable(): + if not args.force: + return 1 + self.print_warning("Test executable not found, but continuing due to --force") + return 0 + + # 4. 运行测试 + self.print_step(4, 4, "Running physics module test") + test_passed = self.run_test_with_intel_env() + + # 生成报告 + test_time = time.time() - start_time + self.print_header("Step 1 Complete") + + print(f"测试: {'通过 ✓' if test_passed else '失败 ✗'}") + print(f"测试程序: {self.test_exe.name if self.test_exe else '未找到'}") + print(f"总耗时: {test_time:.1f}秒") + + if test_passed: + print(f"\n下一步: 更新配置以包含物理设置") + print(f"建议: 修改config.f90,添加physics相关字段") + return 0 + else: + if args.force: + self.print_warning("测试失败,但由于--force标志继续执行") + return 0 + return 1 + + except KeyboardInterrupt: + self.print_error("\n测试被用户中断") + return 1 + except Exception as e: + self.print_error(f"测试过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + system = Step1System() + return system.run() + +if __name__ == "__main__": + # 需要导入argparse + import argparse + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/run_step2.bat b/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/run_step2.bat new file mode 100644 index 00000000..9c1f62de --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/run_step2.bat @@ -0,0 +1,39 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Step 2: Configuration Physics Update +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python step2 script with full Intel environment support... +echo. + +python run_step2.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Step 2 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Step 2 completed successfully! +echo. +echo [INFO] Next step: Update component manager to support physics +echo [INFO] Run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/run_step2.py b/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/run_step2.py new file mode 100644 index 00000000..c16b7608 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/scripts/run_step2.py @@ -0,0 +1,284 @@ +#!/usr/bin/env python3 +""" +Step 2: Configuration Physics Update +测试配置模块的物理功能更新 +""" + +import os +import sys +import subprocess +import time +from pathlib import Path + +# 添加当前目录到路径,以便导入build.py的类 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import IntelEnvironment, BuildSystem +except ImportError: + print("Error: Cannot import build.py. Make sure build.py is in the same directory.") + sys.exit(1) + +class Step2System(BuildSystem): + """Step 2 测试系统,继承自BuildSystem""" + + def __init__(self): + super().__init__() + self.test_name = "test_config_physics" + self.test_exe = None + + def find_test_executable(self): + """查找测试可执行文件""" + possible_paths = [ + self.build_dir / "bin" / "Debug" / f"{self.test_name}.exe", + self.build_dir / "Debug" / f"{self.test_name}.exe", + self.build_dir / f"{self.test_name}.exe", + self.build_dir / "bin" / f"{self.test_name}.exe", + ] + + for path in possible_paths: + if path.exists(): + self.test_exe = path + self.print_success(f"Found test executable: {path}") + return True + + return False + + def run_test_with_intel_env(self): + """在Intel环境下运行测试""" + if not self.test_exe: + self.print_error("No test executable found") + return False + + self.print_step(1, 2, f"Running test: {self.test_exe.name}") + + try: + # 创建临时的批处理文件来运行测试(包含Intel环境) + import tempfile + + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + # 设置Intel环境 + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'echo [INFO] Intel environment configured\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'echo [WARNING] Intel environment not found\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + self.print_info(f"Command: {temp_bat}") + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower() or 'fail' in line.lower() or '✗' in line: + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or 'pass' in line.lower() or '✓' in line: + print(f" \033[92m{line}\033[0m") + elif '=' in line or '---' in line or '===' in line: + print(f" \033[96m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + self.print_warning("Test stderr output:") + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + self.print_step(2, 2, "Test execution completed") + + if result.returncode == 0: + self.print_success("Test passed") + return True + else: + self.print_error(f"Test failed (exit code: {result.returncode})") + return False + + except Exception as e: + self.print_error(f"Failed to run test: {e}") + return False + + def build_project(self, args): + """构建项目""" + if args.no_build: + self.print_info("Skipping build (--no-build flag)") + return True + + self.print_step(1, 3, "Building project") + + # 清理构建目录 + if args.clean and self.build_dir.exists(): + self.print_info("Cleaning build directory...") + import shutil + try: + shutil.rmtree(self.build_dir) + self.print_success("Build directory cleaned") + except Exception as e: + self.print_error(f"Clean failed: {e}") + if not args.force: + return False + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 配置CMake + self.print_step(2, 3, "Configuring CMake") + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-DCMAKE_BUILD_TYPE=Debug", + "-T", "fortran=ifx", + ] + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + if not self.run_command(cmake_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + # 构建项目 + self.print_step(3, 3, "Building project") + build_cmd = [ + "cmake", + "--build", ".", + "--config", "Debug", + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + if not self.run_command(build_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + self.print_success("Build completed") + return True + + def run(self): + """运行Step 2测试""" + import argparse + + parser = argparse.ArgumentParser( + description="Step 2: Configuration Physics Update", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认运行 + %(prog)s --clean # 清理后构建并测试 + %(prog)s --no-build # 只运行测试,不重新构建 + %(prog)s --verbose # 详细输出 + """ + ) + + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-build", action="store_true", + help="不重新构建,直接运行测试") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + + args = parser.parse_args() + + # 开始测试 + start_time = time.time() + + self.print_header("Step 2: Configuration Physics Update") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 设置Intel环境 + self.print_step(1, 4, "Setting up Intel oneAPI environment") + if not self.setup_intel_environment(args): + if not args.force: + self.print_error("Intel environment setup failed") + return 1 + self.print_warning("Intel environment setup failed, continuing...") + + # 2. 构建项目(如果需要) + self.print_step(2, 4, "Building project if needed") + if not self.build_project(args): + if not args.force: + return 1 + + # 3. 查找测试可执行文件 + self.print_step(3, 4, "Finding test executable") + if not self.find_test_executable(): + self.print_error(f"Test executable {self.test_name}.exe not found") + if not args.force: + return 1 + self.print_warning("Test executable not found, but continuing due to --force") + return 0 + + # 4. 运行测试 + self.print_step(4, 4, "Running configuration physics test") + test_passed = self.run_test_with_intel_env() + + # 生成报告 + test_time = time.time() - start_time + self.print_header("Step 2 Complete") + + print(f"测试: {'通过 ✓' if test_passed else '失败 ✗'}") + print(f"测试程序: {self.test_exe.name if self.test_exe else '未找到'}") + print(f"总耗时: {test_time:.1f}秒") + + if test_passed: + print(f"\n下一步: 更新组件管理器以支持物理模块") + print(f"建议: 修改component_manager.f90,添加physics组件创建") + return 0 + else: + if args.force: + self.print_warning("测试失败,但由于--flag继续执行") + return 0 + return 1 + + except KeyboardInterrupt: + self.print_error("\n测试被用户中断") + return 1 + except Exception as e: + self.print_error(f"测试过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + system = Step2System() + return system.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03d/src/CMakeLists.txt new file mode 100644 index 00000000..2aaee245 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/CMakeLists.txt @@ -0,0 +1,18 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(base) +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) +add_subdirectory(physics) # ← 新增物理模块目录 +add_subdirectory(manager) +add_subdirectory(solver) + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/base/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03d/src/base/CMakeLists.txt new file mode 100644 index 00000000..74f4aa65 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/base/CMakeLists.txt @@ -0,0 +1,16 @@ +# src/base/CMakeLists.txt +message(STATUS "Configuring base module...") + +add_library(base STATIC + modules.f90 + precision.f90 # 新增 +) + +set_target_properties(base PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Base module configured") + +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/base/modules.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/src/base/modules.f90 new file mode 100644 index 00000000..43aaee24 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/base/modules.f90 @@ -0,0 +1,36 @@ +! src/base/modules.f90 +module base_modules + use, intrinsic :: iso_fortran_env, only: real64, int32 + implicit none + + public :: wp, ip, max_name_len, string_len, cfd_config_base, component_info + + integer, parameter :: wp = real64 + integer, parameter :: ip = int32 + integer, parameter :: string_len = 100 + integer, parameter :: max_name_len = 32 + + ! 基础配置类型 + type :: cfd_config_base + character(len=max_name_len) :: ic_type = "step" + character(len=max_name_len) :: recon_scheme = "eno" + character(len=max_name_len) :: flux_type = "rusanov" + integer(ip) :: rk_order = 1 + real(wp) :: wave_speed = 1.0_wp + real(wp) :: final_time = 0.625_wp + real(wp) :: dt = 0.025_wp + character(len=max_name_len) :: boundary_type = "periodic" + integer(ip) :: spatial_order = 2 + character(len=max_name_len) :: equation_type = "linear_advection" + character(len=max_name_len) :: problem_type = "linear_advection" + logical :: verbose = .true. + end type cfd_config_base + + ! 组件信息类型 + type :: component_info + character(len=max_name_len) :: category = "" + character(len=max_name_len) :: name = "" + integer(ip) :: order = 0 + end type component_info + +end module base_modules \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/base/precision.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/src/base/precision.f90 new file mode 100644 index 00000000..4ac5fd7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/base/precision.f90 @@ -0,0 +1,9 @@ +! src/base/precision.f90(简单版本) +module precision_module + use base_modules, only: wp, ip + implicit none + + ! 重新导出,确保兼容 + public :: wp, ip + +end module precision_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03d/src/core/CMakeLists.txt new file mode 100644 index 00000000..d8b8df06 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/core/CMakeLists.txt @@ -0,0 +1,14 @@ +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") + +add_library(core STATIC + registry.f90 +) + +target_link_libraries(core PRIVATE base) + +set_target_properties(core PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Core module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/core/factory_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/src/core/factory_base.f90 new file mode 100644 index 00000000..302418a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/core/factory_base.f90 @@ -0,0 +1,57 @@ +! src/core/factory_base.f90 +module factory_base_module + use base_modules, only: wp, ip + use registry_module, only: create_component, has_component + + implicit none + private + public :: wp, ip, factory_base, factory_create + + ! 工厂基类 + type :: factory_base + character(len=max_name_length) :: category = "" + contains + procedure :: create => factory_base_create + procedure :: get_available => factory_base_get_available + end type factory_base + + ! 便捷函数类型 + abstract interface + function factory_function_interface(category, name) result(instance) + import :: wp + character(len=*), intent(in) :: category, name + class(*), allocatable :: instance + end function factory_function_interface + end interface + +contains + + ! 创建工厂实例 + function factory_create(category) result(factory) + character(len=*), intent(in) :: category + type(factory_base) :: factory + factory%category = trim(category) + end function factory_create + + ! 工厂创建方法 + function factory_base_create(this, name) result(instance) + class(factory_base), intent(in) :: this + character(len=*), intent(in) :: name + class(*), allocatable :: instance + + instance = create_component(this%category, name) + end function factory_base_create + + ! 获取可用组件列表(简化版) + subroutine factory_base_get_available(this, names, count) + class(factory_base), intent(in) :: this + character(len=*), allocatable, intent(out) :: names(:) + integer(ip), intent(out) :: count + + ! 这里需要实现从注册表获取列表的逻辑 + ! 暂时返回空列表 + count = 0 + allocate(character(len=max_name_length) :: names(0)) + end subroutine factory_base_get_available + +end module factory_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..a960167c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/src/core/registry.f90 new file mode 100644 index 00000000..d155aa19 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/core/registry.f90 @@ -0,0 +1,257 @@ +! src/core/registry.f90 (更新版) +module registry_module + use base_modules, only: wp, ip, max_name_len, component_info + + implicit none + private + + ! 明确公开所有需要的接口 + public :: wp, ip ! 类型参数 + public :: component_info ! 类型 + public :: registry_init, registry_cleanup ! 初始化/清理 + public :: register_component_simple ! 注册组件 + public :: has_component_simple ! 检查组件 + public :: list_components ! 列出组件 + public :: registry_is_initialized ! 检查初始化状态 + public :: registry_get_size ! 获取大小 + public :: initialize_default_components ! 新增:初始化默认组件 + + ! 全局注册表 + type :: component_registry + type(component_info), allocatable :: components(:) + integer(ip) :: count = 0 + integer(ip) :: capacity = 100 + logical :: initialized = .false. + logical :: verbose = .true. + logical :: default_components_added = .false. ! 新增:标记是否已添加默认组件 + end type component_registry + + type(component_registry) :: registry + +contains + + ! ==================== 公共API ==================== + + subroutine registry_init(verbose) + logical, optional, intent(in) :: verbose + + if (registry%initialized) then + if (registry%verbose) then + print *, "[REGISTRY] Already initialized" + end if + return + end if + + if (present(verbose)) then + registry%verbose = verbose + end if + + allocate(registry%components(registry%capacity)) + registry%initialized = .true. + + if (registry%verbose) then + print *, "[REGISTRY] Initialized with capacity:", registry%capacity + end if + end subroutine registry_init + + subroutine registry_cleanup() + if (allocated(registry%components)) then + deallocate(registry%components) + end if + registry%initialized = .false. + registry%count = 0 + registry%default_components_added = .false. ! 重置标记 + + if (registry%verbose) then + print *, "[REGISTRY] Cleaned up" + end if + end subroutine registry_cleanup + + ! 新增:初始化默认组件 + subroutine initialize_default_components() + if (.not. registry%initialized) then + call registry_init() + end if + + if (registry%default_components_added) then + if (registry%verbose) then + print *, "[REGISTRY] Default components already added" + end if + return + end if + + ! 注册重构器 + call register_component_simple("reconstructor", "eno", order=3) + call register_component_simple("reconstructor", "weno3", order=3) + call register_component_simple("reconstructor", "weno5", order=5) + + ! 注册通量计算器 + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + + ! 注册边界条件 + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + + ! 注册时间积分器 + call register_component_simple("integrator", "rk1", order=1) + call register_component_simple("integrator", "rk2", order=2) + call register_component_simple("integrator", "rk3", order=3) + + ! 注册方程 + call register_component_simple("equation", "linear_advection") + + ! 注册问题 + call register_component_simple("problem", "linear_advection") + + ! 注册初始条件 + call register_component_simple("initial_condition", "step") + call register_component_simple("initial_condition", "sin") + call register_component_simple("initial_condition", "gaussian") + + registry%default_components_added = .true. + + if (registry%verbose) then + print *, "[REGISTRY] Default components registered" + print *, "[REGISTRY] Total components:", registry%count + end if + end subroutine initialize_default_components + + subroutine register_component_simple(category, name, order) + character(len=*), intent(in) :: category, name + integer(ip), optional, intent(in) :: order + + integer(ip) :: i + type(component_info) :: info + + if (.not. registry%initialized) then + call registry_init() + end if + + ! 检查是否已存在 + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + if (registry%verbose) then + print *, "[WARN] Overwriting component: ", trim(category), ".", trim(name) + end if + + ! 更新 + if (present(order)) then + registry%components(i)%order = order + else + registry%components(i)%order = 0 + end if + return + end if + end do + + ! 扩展数组 + if (registry%count >= registry%capacity) then + call expand_registry() + end if + + ! 添加新组件 + registry%count = registry%count + 1 + + info%category = trim(category) + info%name = trim(name) + info%order = 0 + if (present(order)) then + info%order = order + end if + + registry%components(registry%count) = info + + if (registry%verbose) then + print *, "[OK] Registered simple: ", trim(category), ".", trim(name) + end if + end subroutine register_component_simple + + logical function has_component_simple(category, name) + character(len=*), intent(in) :: category, name + + integer(ip) :: i + + has_component_simple = .false. + + if (.not. registry%initialized) return + + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + has_component_simple = .true. + return + end if + end do + end function has_component_simple + + subroutine list_components(category) + character(len=*), optional, intent(in) :: category + + integer(ip) :: i, count + + if (.not. registry%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + if (registry%count == 0) then + print *, "[INFO] No components registered" + return + end if + + count = 0 + print *, "=== Registry Contents ===" + do i = 1, registry%count + if (.not. present(category) .or. & + trim(registry%components(i)%category) == trim(category)) then + call print_component_info(registry%components(i)) + count = count + 1 + end if + end do + + print *, "Total:", count, "components" + print *, "==========================" + end subroutine list_components + + ! ==================== 新增函数 ==================== + + logical function registry_is_initialized() + ! 检查注册表是否已初始化 + registry_is_initialized = registry%initialized + end function registry_is_initialized + + integer(ip) function registry_get_size() + ! 获取注册表中的组件数量 + registry_get_size = registry%count + end function registry_get_size + + ! ==================== 内部辅助函数 ==================== + + subroutine expand_registry() + type(component_info), allocatable :: temp(:) + + registry%capacity = registry%capacity * 2 + allocate(temp(registry%capacity)) + temp(1:registry%count) = registry%components(1:registry%count) + call move_alloc(temp, registry%components) + + if (registry%verbose) then + print *, "[INFO] Registry expanded to capacity:", registry%capacity + end if + end subroutine expand_registry + + subroutine print_component_info(info) + type(component_info), intent(in) :: info + + if (info%order > 0) then + print *, " [", trim(info%category), ".", trim(info%name), & + " (order:", info%order, ")]" + else + print *, " [", trim(info%category), ".", trim(info%name), "]" + end if + end subroutine print_component_info + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/core/registry_initializer.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/src/core/registry_initializer.f90 new file mode 100644 index 00000000..44023d1d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/core/registry_initializer.f90 @@ -0,0 +1,39 @@ +! src/core/registry_initializer.f90 (新增文件) +module registry_initializer_module + use registry_module, only: register_component_simple + implicit none + private + public :: initialize_default_registry + +contains + + subroutine initialize_default_registry() + ! 注册重构器 + call register_component_simple("reconstructor", "eno", order=3) + call register_component_simple("reconstructor", "weno3", order=3) + call register_component_simple("reconstructor", "weno5", order=5) + + ! 注册通量计算器 + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + + ! 注册边界条件 + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + + ! 注册时间积分器 + call register_component_simple("integrator", "rk1", order=1) + call register_component_simple("integrator", "rk2", order=2) + call register_component_simple("integrator", "rk3", order=3) + + ! 注册方程 + call register_component_simple("equation", "linear_advection") + + ! 注册问题 + call register_component_simple("problem", "linear_advection") + + print *, "[REGISTRY] Default components registered" + end subroutine initialize_default_registry + +end module registry_initializer_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03d/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..70cbbd2f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/infrastructure/CMakeLists.txt @@ -0,0 +1,17 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "Configuring infrastructure module...") + +add_library(infrastructure STATIC + config.f90 + mesh.f90 + domain.f90 # 新增 + solution.f90 # 新增 +) + +target_link_libraries(infrastructure PRIVATE base) + +set_target_properties(infrastructure PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Infrastructure module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/src/infrastructure/config.f90 new file mode 100644 index 00000000..7586a1a5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/infrastructure/config.f90 @@ -0,0 +1,144 @@ +! src/infrastructure/config.f90 (修复版) +module config_module + use base_modules, only: wp, ip, max_name_len, cfd_config_base + + implicit none + public :: wp, ip, cfd_config, config_print, config_with_reconstruction + + ! 扩展配置类型 - 添加物理相关字段 + type, extends(cfd_config_base) :: cfd_config + ! 物理参数 + real(wp) :: left_boundary_value = 1.0_wp + real(wp) :: right_boundary_value = 2.0_wp + real(wp) :: domain_length = 2.0_wp + + ! 新增:物理模块相关配置 + real(wp) :: pulse_center = 0.5_wp ! 高斯脉冲中心 + real(wp) :: pulse_width = 0.1_wp ! 高斯脉冲宽度 + logical :: enable_physics = .true. ! 是否启用物理模块 + contains + ! 新增:物理相关配置方法 + procedure :: set_physics_parameters + procedure :: get_physics_info + end type cfd_config + +contains + + subroutine config_print(cfg) + type(cfd_config), intent(in) :: cfg + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(cfg%ic_type) + print *, "Reconstruction: ", trim(cfg%recon_scheme), " (order:", cfg%spatial_order, ")" + print *, "Flux type: ", trim(cfg%flux_type) + print *, "Time integration: RK", cfg%rk_order + print *, "Wave speed: ", cfg%wave_speed + print *, "Final time: ", cfg%final_time + print *, "Time step: ", cfg%dt + print *, "Boundary: ", trim(cfg%boundary_type) + + ! 新增:物理配置信息 + print *, "--- Physics Configuration ---" + print *, "Equation type: ", trim(cfg%equation_type) + print *, "Problem type: ", trim(cfg%problem_type) + print *, "Domain length: ", cfg%domain_length + print *, "Physics enabled: ", cfg%enable_physics + + if (cfg%ic_type == "gaussian") then + print *, "Pulse center: ", cfg%pulse_center + print *, "Pulse width: ", cfg%pulse_width + end if + + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(cfg, scheme, order) + type(cfd_config), intent(inout) :: cfg + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + integer :: i + + ! 转换为小写 + cfg%recon_scheme = scheme + do i = 1, len_trim(cfg%recon_scheme) + if (cfg%recon_scheme(i:i) >= 'A' .and. cfg%recon_scheme(i:i) <= 'Z') then + cfg%recon_scheme(i:i) = char(ichar(cfg%recon_scheme(i:i)) + 32) + end if + end do + + ! 设置阶数 + if (present(order)) then + cfg%spatial_order = order + else + if (index(cfg%recon_scheme, 'weno') > 0) then + cfg%spatial_order = 5 + else if (trim(cfg%recon_scheme) == 'eno') then + cfg%spatial_order = 3 + end if + end if + + if (cfg%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(cfg%recon_scheme), & + " Order: ", cfg%spatial_order + end if + end subroutine config_with_reconstruction + + ! ========== 新增:物理参数设置方法 ========== + + subroutine set_physics_parameters(this, equation_type, problem_type, & + domain_length, enable_physics) + class(cfd_config), intent(inout) :: this + character(len=*), intent(in), optional :: equation_type, problem_type + real(wp), intent(in), optional :: domain_length + logical, intent(in), optional :: enable_physics + + if (present(equation_type)) then + this%equation_type = trim(equation_type) + if (this%verbose) then + print *, "[CONFIG] Set equation type: ", trim(this%equation_type) + end if + end if + + if (present(problem_type)) then + this%problem_type = trim(problem_type) + if (this%verbose) then + print *, "[CONFIG] Set problem type: ", trim(this%problem_type) + end if + end if + + if (present(domain_length)) then + this%domain_length = domain_length + if (this%verbose) then + print *, "[CONFIG] Set domain length: ", this%domain_length + end if + end if + + if (present(enable_physics)) then + this%enable_physics = enable_physics + if (this%verbose) then + print *, "[CONFIG] Physics module enabled: ", this%enable_physics + end if + end if + end subroutine set_physics_parameters + + subroutine get_physics_info(this) + class(cfd_config), intent(in) :: this + + print *, "=== Physics Configuration Info ===" + print *, "Equation type: ", trim(this%equation_type) + print *, "Problem type: ", trim(this%problem_type) + print *, "Domain length: ", this%domain_length + print *, "Wave speed: ", this%wave_speed + print *, "Physics enabled: ", this%enable_physics + + if (this%ic_type == "gaussian") then + print *, "Pulse parameters:" + print *, " Center: ", this%pulse_center + print *, " Width: ", this%pulse_width + end if + + print *, "==================================" + end subroutine get_physics_info + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/infrastructure/domain.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/src/infrastructure/domain.f90 new file mode 100644 index 00000000..c3662f03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/infrastructure/domain.f90 @@ -0,0 +1,102 @@ +! src/infrastructure/domain.f90 +module domain_module + use base_modules, only: wp, ip, max_name_len + use config_module, only: cfd_config + use mesh_module, only: mesh_type + + implicit none + private + public :: wp, ip, domain_type, domain_create, is_physical_cell + + type :: domain_type + type(cfd_config), pointer :: config => null() + type(mesh_type), pointer :: mesh => null() + integer(ip) :: nghosts = 0 + integer(ip) :: ist = 1 ! 物理区域起始索引(1-based) + integer(ip) :: ied = 1 ! 物理区域结束索引(exclusive) + integer(ip) :: ntcells = 0 ! 总单元数(含ghost) + contains + procedure :: print_info => domain_print_info + procedure :: get_physical_indices => domain_get_physical_indices + end type domain_type + +contains + + function domain_create(config, mesh) result(domain) + type(cfd_config), target, intent(in) :: config + type(mesh_type), target, intent(in) :: mesh + type(domain_type) :: domain + + domain%config => config + domain%mesh => mesh + + ! 计算ghost层数(参考Julia的_calc_nghosts) + domain%nghosts = calc_nghosts(config) + domain%ist = domain%nghosts + 1 + domain%ied = domain%ist + mesh%ncells + domain%ntcells = mesh%ncells + 2 * domain%nghosts + + if (config%verbose) then + print *, "[DOMAIN] Created:" + print *, " Ghost layers: ", domain%nghosts + print *, " Physical cells: ", domain%ist, " to ", domain%ied - 1 + print *, " Total cells: ", domain%ntcells + end if + end function domain_create + + function calc_nghosts(config) result(nghosts) + type(cfd_config), intent(in) :: config + integer(ip) :: nghosts + + character(len=max_name_len) :: scheme + + scheme = config%recon_scheme + + if (scheme == "eno") then + nghosts = config%spatial_order + else if (index(scheme, "weno") > 0) then + nghosts = config%spatial_order / 2 + 1 + else + print *, "[WARNING] Unknown scheme, using default nghosts=2" + nghosts = 2 + end if + + if (nghosts <= 0) then + print *, "[ERROR] Invalid nghosts: ", nghosts + nghosts = 2 + end if + end function calc_nghosts + + logical function is_physical_cell(this, idx) + class(domain_type), intent(in) :: this + integer(ip), intent(in) :: idx + is_physical_cell = (idx >= this%ist .and. idx < this%ied) + end function is_physical_cell + + function domain_get_physical_indices(this) result(indices) + class(domain_type), intent(in) :: this + integer(ip), allocatable :: indices(:) + integer(ip) :: i, count + + count = this%ied - this%ist + allocate(indices(count)) + + do i = 1, count + indices(i) = this%ist + i - 1 + end do + end function domain_get_physical_indices + + subroutine domain_print_info(this) + class(domain_type), intent(in) :: this + + print *, "=== Domain Information ===" + print *, "Configuration: ", trim(this%config%recon_scheme), & + " order ", this%config%spatial_order + print *, "Ghost layers: ", this%nghosts + print *, "Physical cells: ", this%ist, " to ", this%ied - 1 + print *, "Total cells: ", this%ntcells + print *, "Mesh cells: ", this%mesh%ncells + print *, "==========================" + end subroutine domain_print_info + +end module domain_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..f810f3a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/infrastructure/mesh.f90 @@ -0,0 +1,73 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use base_modules, only: wp, ip + + implicit none + public :: wp, ip, mesh_type, mesh_init, mesh_print_info + + ! 网格类型 + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer(ip) :: ncells = 40 + integer(ip) :: nnodes + integer(ip) :: nx + real(wp), allocatable :: x(:) ! 节点坐标 + real(wp), allocatable :: xcc(:) ! 单元中心坐标 + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer(ip), optional, intent(in) :: ncells + + integer(ip) :: i + + ! 设置参数 + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! 计算 + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! 分配内存 + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! 生成节点坐标 + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! 生成单元中心坐标 + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== Mesh Information ===" + print *, "Domain: [", this%xmin, ", ", this%xmax, "]" + print *, "Cells: ", this%ncells + print *, "Nodes: ", this%nnodes + print *, "dx: ", this%dx + print *, "L: ", this%L + print *, "========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/infrastructure/solution.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/src/infrastructure/solution.f90 new file mode 100644 index 00000000..ce88fd8a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/infrastructure/solution.f90 @@ -0,0 +1,131 @@ +! src/infrastructure/solution.f90 +module solution_module + use base_modules, only: wp, ip + use domain_module, only: domain_type + + implicit none + private + public :: wp, ip, solution_type, solution_create, solution_reset + + type :: solution_type + type(domain_type), pointer :: domain => null() + real(wp), allocatable :: u(:) ! 当前解(含ghost) + real(wp), allocatable :: un(:) ! 旧解 + real(wp), allocatable :: q_face_left(:) ! 左界面值 + real(wp), allocatable :: q_face_right(:)! 右界面值 + real(wp), allocatable :: flux(:) ! 通量 + real(wp), allocatable :: res(:) ! 残差 + contains + procedure :: initialize => solution_initialize + procedure :: update_old_field => solution_update_old_field + procedure :: print_info => solution_print_info + procedure :: reset => solution_reset_instance + end type solution_type + +contains + + function solution_create(domain) result(solution) + type(domain_type), target, intent(in) :: domain + type(solution_type) :: solution + + integer(ip) :: ncells, nnodes, ntcells + + solution%domain => domain + + ncells = domain%mesh%ncells + nnodes = domain%mesh%nnodes + ntcells = domain%ntcells + + ! 分配数组(与Julia solution.jl一致) + allocate(solution%u(ntcells), source=0.0_wp) + allocate(solution%un(ntcells), source=0.0_wp) + allocate(solution%q_face_left(nnodes), source=0.0_wp) + allocate(solution%q_face_right(nnodes), source=0.0_wp) + allocate(solution%flux(nnodes), source=0.0_wp) + allocate(solution%res(ncells), source=0.0_wp) + + if (domain%config%verbose) then + print *, "[SOLUTION] Created:" + print *, " u size: ", size(solution%u), " (with ghosts)" + print *, " flux size: ", size(solution%flux) + print *, " res size: ", size(solution%res) + end if + end function solution_create + + subroutine solution_initialize(this, initial_values) + class(solution_type), intent(inout) :: this + real(wp), intent(in), optional :: initial_values(:) + + integer(ip) :: i, idx + type(domain_type), pointer :: domain + + domain => this%domain + + if (present(initial_values)) then + ! 应用初始值到物理区域 + do i = domain%ist, domain%ied - 1 + idx = i - domain%ist + 1 + if (idx <= size(initial_values)) then + this%u(i) = initial_values(idx) + end if + end do + else + ! 默认为0 + this%u = 0.0_wp + end if + + ! 同步旧场(与Julia的update_old_field一致) + call this%update_old_field() + + if (domain%config%verbose) then + print *, "[SOLUTION] Initialized" + print *, " u range: ", minval(this%u), " to ", maxval(this%u) + end if + end subroutine solution_initialize + + subroutine solution_update_old_field(this) + class(solution_type), intent(inout) :: this + this%un = this%u ! 与Julia的 un .= u 一致 + end subroutine solution_update_old_field + + subroutine solution_reset_instance(this) + class(solution_type), intent(inout) :: this + call solution_reset(this) + end subroutine solution_reset_instance + + subroutine solution_reset(solution) + type(solution_type), intent(inout) :: solution + + if (allocated(solution%u)) solution%u = 0.0_wp + if (allocated(solution%un)) solution%un = 0.0_wp + if (allocated(solution%q_face_left)) solution%q_face_left = 0.0_wp + if (allocated(solution%q_face_right)) solution%q_face_right = 0.0_wp + if (allocated(solution%flux)) solution%flux = 0.0_wp + if (allocated(solution%res)) solution%res = 0.0_wp + + if (associated(solution%domain) .and. solution%domain%config%verbose) then + print *, "[SOLUTION] Reset" + end if + end subroutine solution_reset + + subroutine solution_print_info(this) + class(solution_type), intent(in) :: this + + print *, "=== Solution Information ===" + print *, "Arrays:" + print *, " u: ", size(this%u), " elements" + print *, " un: ", size(this%un), " elements" + print *, " q_face_left: ", size(this%q_face_left), " elements" + print *, " q_face_right: ", size(this%q_face_right), " elements" + print *, " flux: ", size(this%flux), " elements" + print *, " res: ", size(this%res), " elements" + + if (allocated(this%u)) then + print *, "Values:" + print *, " u min/max: ", minval(this%u), maxval(this%u) + print *, " un min/max: ", minval(this%un), maxval(this%un) + end if + print *, "============================" + end subroutine solution_print_info + +end module solution_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/manager/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03d/src/manager/CMakeLists.txt new file mode 100644 index 00000000..00c8bf49 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/manager/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/manager/CMakeLists.txt +message(STATUS "配置管理器模块...") + +# 创建管理器库 +add_library(manager STATIC + component_manager.f90 + component_factory.f90 +) + +# 明确依赖关系:管理器依赖所有其他模块 +target_link_libraries(manager + PRIVATE + core + infrastructure + reconstructor + flux +) + +# 设置模块输出目录 +set_target_properties(manager PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "管理器模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/manager/component_factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/src/manager/component_factory.f90 new file mode 100644 index 00000000..de8cbf1a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/manager/component_factory.f90 @@ -0,0 +1,142 @@ +! src/manager/component_factory.f90 (完整文件) +module component_factory_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + use eno_reconstructor_module, only: eno_reconstructor, create_eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor, create_weno3_reconstructor + use weno5_reconstructor_module, only: weno5_reconstructor, create_weno5_reconstructor + use rusanov_flux_module, only: rusanov_flux, create_rusanov_flux + + implicit none + private + public :: wp, create_reconstructor, create_flux_calculator + + ! 错误代码 + integer, parameter :: CM_SUCCESS = 0 + integer, parameter :: CM_ERROR_UNKNOWN_SCHEME = 1 + integer, parameter :: CM_ERROR_UNKNOWN_FLUX = 2 + integer, parameter :: CM_ERROR_INVALID_ORDER = 3 + +contains + + ! ==================== 重构器创建 ==================== + + function create_reconstructor(config, status) result(recon) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(reconstructor_base), allocatable :: recon + + character(len=20) :: scheme + integer :: order, error_code + + scheme = trim(adjustl(config%recon_scheme)) + order = config%spatial_order + + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating reconstructor: ", scheme, " order=", order + end if + + ! 处理"weno"作为WENO5的别名(与Julia一致) + if (scheme == "weno" .and. order == 5) then + scheme = "weno5" + end if + + select case(scheme) + case('eno') + allocate(eno_reconstructor :: recon) + select type(recon) + type is(eno_reconstructor) + recon = create_eno_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case('weno3') + allocate(weno3_reconstructor :: recon) + select type(recon) + type is(weno3_reconstructor) + recon = create_weno3_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case('weno5') + allocate(weno5_reconstructor :: recon) + select type(recon) + type is(weno5_reconstructor) + recon = create_weno5_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case default + error_code = CM_ERROR_UNKNOWN_SCHEME + if (config%verbose) then + print *, "[ERROR] Unknown reconstructor scheme: ", scheme + print *, " Available: eno, weno3, weno5" + end if + end select + + ! 检查阶数有效性 + if (error_code == CM_SUCCESS) then + if (order < 1) then + error_code = CM_ERROR_INVALID_ORDER + if (config%verbose) then + print *, "[ERROR] Invalid spatial order: ", order + end if + end if + end if + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Reconstructor creation failed" + end if + end function create_reconstructor + + ! ==================== 通量计算器创建 ==================== + + function create_flux_calculator(config, status) result(flux) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(flux_calculator_base), allocatable :: flux + + character(len=20) :: flux_type + integer :: error_code + + flux_type = trim(adjustl(config%flux_type)) + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating flux calculator: ", flux_type + end if + + select case(flux_type) + case('rusanov') + allocate(rusanov_flux :: flux) + select type(flux) + type is(rusanov_flux) + flux = create_rusanov_flux() + flux%wave_speed_default = config%wave_speed + end select + + case default + error_code = CM_ERROR_UNKNOWN_FLUX + if (config%verbose) then + print *, "[ERROR] Unknown flux type: ", flux_type + print *, " Available: rusanov" + end if + end select + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Flux calculator creation failed" + end if + end function create_flux_calculator + +end module component_factory_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/manager/component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/src/manager/component_manager.f90 new file mode 100644 index 00000000..25eac29b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/manager/component_manager.f90 @@ -0,0 +1,76 @@ +! src/manager/component_manager.f90 (完整文件) +module component_manager_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use component_factory_module, only: create_reconstructor, create_flux_calculator + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + implicit none + private + public :: wp, component_manager_info, validate_config + public :: create_reconstructor, create_flux_calculator + +contains + + ! ==================== 配置验证 ==================== + + function validate_config(config) result(is_valid) + type(cfd_config), intent(in) :: config + logical :: is_valid + + integer :: status + class(reconstructor_base), allocatable :: test_recon + class(flux_calculator_base), allocatable :: test_flux + + is_valid = .false. + + ! 测试创建重构器 + test_recon = create_reconstructor(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid reconstructor configuration" + end if + return + end if + + ! 测试创建通量计算器 + test_flux = create_flux_calculator(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid flux configuration" + end if + return + end if + + ! 清理测试组件 + if (allocated(test_recon)) deallocate(test_recon) + if (allocated(test_flux)) deallocate(test_flux) + + is_valid = .true. + + if (config%verbose) then + print *, "[CONFIG VALIDATION] Configuration is valid" + end if + end function validate_config + + ! ==================== 信息显示 ==================== + + subroutine component_manager_info() + print *, "=== Component Manager ===" + print *, "Available reconstructors:" + print *, " - eno (orders: 1-7)" + print *, " - weno3 (order: 3)" + print *, " - weno5 (order: 5)" + print *, "" + print *, "Available flux calculators:" + print *, " - rusanov" + print *, "" + print *, "Features:" + print *, " - Configuration validation" + print *, " - Component creation from config" + print *, " - Error handling with status codes" + print *, "=========================" + end subroutine component_manager_info + +end module component_manager_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..3fde7746 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,28 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux STATIC + base.f90 + rusanov.f90 +) + +#target_link_libraries(flux PRIVATE core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 明确设置不使用 /Qm64 选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + set_target_properties(flux PROPERTIES + Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /Qm64" # 明确设置,避免继承问题 + ) +endif() + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/flux/base.f90 new file mode 100644 index 00000000..7080a7ae --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/flux/base.f90 @@ -0,0 +1,30 @@ +!src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + procedure :: print_basic_info => flux_print_basic ! 添加辅助方法 + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + + subroutine flux_print_basic(this) + class(flux_calculator_base), intent(in) :: this + print *, " Name: ", trim(this%name) + end subroutine flux_print_basic + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..daa9e3bb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/flux/rusanov.f90 @@ -0,0 +1,41 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux, create_rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + + ! 构造函数接口 + interface rusanov_flux + module procedure create_rusanov_flux + end interface + +contains + + ! 构造函数 + type(rusanov_flux) function create_rusanov_flux() result(this) + this%name = "Rusanov" + this%wave_speed_default = 1.0_real64 + end function create_rusanov_flux + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Flux calculator information:" + call this%print_basic_info() + + ! 添加Rusanov特有信息 + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..c88ea647 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,23 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor STATIC + base.f90 + eno.f90 + weno3.f90 + weno5.f90 +) + +target_link_libraries(reconstructor PRIVATE core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..53798d02 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/reconstructor/base.f90 @@ -0,0 +1,33 @@ +!src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + procedure :: print_basic_info => reconstructor_print_basic ! 添加一个辅助方法 + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + + subroutine reconstructor_print_basic(this) + class(reconstructor_base), intent(in) :: this + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_print_basic + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..f973e8b3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor, create_eno_reconstructor ! ← 添加这个 + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + + ! 构造函数接口 + interface eno_reconstructor + module procedure create_eno_reconstructor + end interface + +contains + + ! 构造函数 + type(eno_reconstructor) function create_eno_reconstructor() result(this) + this%name = "ENO" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_eno_reconstructor + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加ENO特有信息 + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..d5b7a747 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno3_reconstructor, create_weno3_reconstructor + + type, extends(reconstructor_base) :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + + ! 构造函数接口 + interface weno3_reconstructor + module procedure create_weno3_reconstructor + end interface + +contains + + ! 构造函数 + type(weno3_reconstructor) function create_weno3_reconstructor() result(this) + this%name = "WENO3" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_weno3_reconstructor + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO3特有信息 + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/reconstructor/weno5.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/reconstructor/weno5.f90 new file mode 100644 index 00000000..a869c67d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/numerics/reconstructor/weno5.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/weno5.f90(新增) +module weno5_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno5_reconstructor, create_weno5_reconstructor + + type, extends(reconstructor_base) :: weno5_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno5_info + end type weno5_reconstructor + + ! 构造函数接口 + interface weno5_reconstructor + module procedure create_weno5_reconstructor + end interface + +contains + + ! 构造函数 + type(weno5_reconstructor) function create_weno5_reconstructor() result(this) + this%name = "WENO5" + this%order = 5 + this%epsilon = 1.0e-6_real64 + end function create_weno5_reconstructor + + subroutine weno5_info(this) + class(weno5_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO5特有信息 + print *, " Type: WENO-5 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno5_info + +end module weno5_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/physics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03d/src/physics/CMakeLists.txt new file mode 100644 index 00000000..cc4e233a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/physics/CMakeLists.txt @@ -0,0 +1,19 @@ +# src/physics/CMakeLists.txt +message(STATUS "配置物理模块...") + +# 创建物理模块库 +add_library(physics STATIC + physics_interface.f90 + equations/linear_convection.f90 + problems/linear_convection_problem.f90 +) + +# 链接依赖 +target_link_libraries(physics PRIVATE base) + +# 设置模块输出目录 +set_target_properties(physics PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "物理模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/physics/equations/linear_convection.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/src/physics/equations/linear_convection.f90 new file mode 100644 index 00000000..fff7be55 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/physics/equations/linear_convection.f90 @@ -0,0 +1,49 @@ +! src/physics/equations/linear_convection.f90 +module linear_convection_equation + use precision_module, only: wp, ip + use physics_interface, only: physics_equation + implicit none + private + + ! 具体方程类型 - 先声明 + type, extends(physics_equation) :: linear_convection_eq + real(wp) :: wave_speed = 1.0_wp + contains + procedure :: flux => lc_flux + procedure :: speed => lc_speed + end type linear_convection_eq + + ! 公开接口 + public :: wp, ip + public :: linear_convection_eq, create_linear_convection_eq + +contains + + ! 构造函数 + function create_linear_convection_eq(wave_speed) result(eq) + real(wp), intent(in), optional :: wave_speed + type(linear_convection_eq) :: eq + + eq%name = "Linear Convection" + if (present(wave_speed)) then + eq%wave_speed = wave_speed + else + eq%wave_speed = 1.0_wp + end if + end function create_linear_convection_eq + + ! 方法实现 + pure function lc_flux(this, u) result(f) + class(linear_convection_eq), intent(in) :: this + real(wp), intent(in) :: u + real(wp) :: f + f = this%wave_speed * u + end function lc_flux + + pure function lc_speed(this) result(a) + class(linear_convection_eq), intent(in) :: this + real(wp) :: a + a = this%wave_speed + end function lc_speed + +end module linear_convection_equation \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/physics/physics_interface.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/src/physics/physics_interface.f90 new file mode 100644 index 00000000..45002da6 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/physics/physics_interface.f90 @@ -0,0 +1,64 @@ +! src/physics/physics_interface.f90 +module physics_interface + use precision_module, only: wp, ip + implicit none + private + + ! 定义抽象基类型 - 先声明为私有,然后在公开部分导出 + type, abstract :: physics_equation + character(len=:), allocatable :: name + contains + procedure(eq_flux_abs), deferred :: flux + procedure(eq_speed_abs), deferred :: speed + end type physics_equation + + type, abstract :: physics_problem + character(len=:), allocatable :: name + contains + procedure(prob_ic_abs), deferred :: initial_condition + procedure(prob_bc_abs), deferred :: boundary_condition + procedure(prob_exact_abs), deferred :: exact_solution + end type physics_problem + + ! 抽象接口定义 + abstract interface + pure function eq_flux_abs(this, u) result(f) + import :: physics_equation, wp + class(physics_equation), intent(in) :: this + real(wp), intent(in) :: u + real(wp) :: f + end function eq_flux_abs + + pure function eq_speed_abs(this) result(a) + import :: physics_equation, wp + class(physics_equation), intent(in) :: this + real(wp) :: a + end function eq_speed_abs + + subroutine prob_ic_abs(this, x, u) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp), intent(out) :: u(:) + end subroutine prob_ic_abs + + subroutine prob_bc_abs(this, u, t) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(inout) :: u(:) + real(wp), intent(in), optional :: t + end subroutine prob_bc_abs + + function prob_exact_abs(this, x, t) result(u) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(in) :: x(:), t + real(wp), dimension(size(x)) :: u + end function prob_exact_abs + end interface + + ! 公开接口 - 使用独立的public语句 + public :: wp, ip + public :: physics_equation, physics_problem + +end module physics_interface \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/physics/problems/linear_convection_problem.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/src/physics/problems/linear_convection_problem.f90 new file mode 100644 index 00000000..06226ed1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/physics/problems/linear_convection_problem.f90 @@ -0,0 +1,118 @@ +! src/physics/problems/linear_convection_problem.f90 +module linear_convection_problem + use precision_module, only: wp, ip + use physics_interface, only: physics_problem + implicit none + private + + ! 具体问题类型 - 先声明 + type, extends(physics_problem) :: linear_convection_prob + real(wp) :: wave_speed = 1.0_wp + real(wp) :: domain_length = 2.0_wp + character(len=20) :: ic_type = "step" + character(len=20) :: boundary_type = "periodic" + contains + procedure :: initial_condition => lc_initial_condition + procedure :: boundary_condition => lc_boundary_condition + procedure :: exact_solution => lc_exact_solution + end type linear_convection_prob + + ! 公开接口 + public :: wp, ip + public :: linear_convection_prob, create_linear_convection_prob + +contains + + ! 构造函数 + function create_linear_convection_prob(wave_speed, domain_length, & + ic_type, boundary_type) result(prob) + real(wp), intent(in), optional :: wave_speed, domain_length + character(len=*), intent(in), optional :: ic_type, boundary_type + type(linear_convection_prob) :: prob + + prob%name = "Linear Convection Problem" + + if (present(wave_speed)) prob%wave_speed = wave_speed + if (present(domain_length)) prob%domain_length = domain_length + if (present(ic_type)) prob%ic_type = ic_type + if (present(boundary_type)) prob%boundary_type = boundary_type + end function create_linear_convection_prob + + ! 初始条件 + subroutine lc_initial_condition(this, x, u) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp), intent(out) :: u(:) + + integer :: i + + select case (trim(this%ic_type)) + case ("step") + do i = 1, size(x) + if (x(i) >= 0.5_wp .and. x(i) <= 1.0_wp) then + u(i) = 2.0_wp + else + u(i) = 1.0_wp + end if + end do + + case ("sin", "sine") + do i = 1, size(x) + u(i) = sin(2.0_wp * 3.141592653589793_wp * x(i) / this%domain_length) + end do + + case ("gaussian") + do i = 1, size(x) + u(i) = exp(-((x(i) - 0.5_wp) / 0.1_wp)**2) + end do + + case default + ! 默认阶跃函数 + do i = 1, size(x) + if (x(i) >= 0.5_wp .and. x(i) <= 1.0_wp) then + u(i) = 2.0_wp + else + u(i) = 1.0_wp + end if + end do + end select + end subroutine lc_initial_condition + + ! 边界条件(虚拟实现,实际在boundary模块) + subroutine lc_boundary_condition(this, u, t) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(inout) :: u(:) + real(wp), intent(in), optional :: t + + ! 边界条件将在独立模块实现 + print *, "[PROBLEM] Boundary condition placeholder" + if (present(t)) then + print *, " Time = ", t + end if + end subroutine lc_boundary_condition + + ! 精确解(周期性平移) + function lc_exact_solution(this, x, t) result(u) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(in) :: x(:), t + real(wp), dimension(size(x)) :: u + real(wp), dimension(size(x)) :: x_shifted + integer :: i + + ! 周期性平移 + do i = 1, size(x) + x_shifted(i) = x(i) - this%wave_speed * t + ! 确保在 [0, domain_length) 范围内 + do while (x_shifted(i) < 0.0_wp) + x_shifted(i) = x_shifted(i) + this%domain_length + end do + do while (x_shifted(i) >= this%domain_length) + x_shifted(i) = x_shifted(i) - this%domain_length + end do + end do + + ! 重用初始条件函数 + call this%initial_condition(x_shifted, u) + end function lc_exact_solution + +end module linear_convection_problem \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/solver/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03d/src/solver/CMakeLists.txt new file mode 100644 index 00000000..f8499eec --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/solver/CMakeLists.txt @@ -0,0 +1,21 @@ +# src/solver/CMakeLists.txt +message(STATUS "配置求解器模块...") + +add_library(solver STATIC + base.f90 + physics_solver.f90 +) + +target_link_libraries(solver + PRIVATE + infrastructure + core + physics + manager +) + +set_target_properties(solver PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "求解器模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/solver/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/src/solver/base.f90 new file mode 100644 index 00000000..cfd78c47 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/solver/base.f90 @@ -0,0 +1,264 @@ +! src/solver/base.f90 +module solver_base_module + use base_modules, only: wp => wp, ip => ip ! 重命名以避免冲突 + + use config_module, only: cfd_config + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create + + implicit none + private + + ! 明确导出列表 + public :: wp, ip ! 类型参数 + public :: solver_base, create_solver_base ! 类型和构造函数 + public :: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, SOLVER_RUNNING + public :: SOLVER_COMPLETED, SOLVER_ERROR ! 状态常量 + + ! 求解器状态枚举 + integer, parameter :: SOLVER_UNINITIALIZED = 0 + integer, parameter :: SOLVER_INITIALIZED = 1 + integer, parameter :: SOLVER_RUNNING = 2 + integer, parameter :: SOLVER_COMPLETED = 3 + integer, parameter :: SOLVER_ERROR = 4 + + ! 求解器基类 + type :: solver_base + ! 基本组件 + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + + ! 状态管理 + integer :: state = SOLVER_UNINITIALIZED + character(len=100) :: error_message = "" + real(wp) :: current_time = 0.0_wp + integer(ip) :: current_step = 0 + + ! 时间控制 + real(wp) :: dt_original = 0.0_wp + contains + procedure :: initialize => solver_base_initialize + procedure :: step => solver_base_step + procedure :: run_to_time => solver_base_run_to_time + procedure :: cleanup => solver_base_cleanup + procedure :: get_state => solver_base_get_state + procedure :: get_error => solver_base_get_error + procedure :: print_info => solver_base_print_info + end type solver_base + + ! 构造函数接口 + interface solver_base + module procedure create_solver_base + end interface + +contains + + ! ==================== 构造函数 ==================== + + function create_solver_base(config, mesh) result(solver) + type(cfd_config), intent(in) :: config + type(mesh_type), intent(in) :: mesh + type(solver_base) :: solver + + solver%config = config + solver%mesh = mesh + + ! 创建域 + solver%domain = domain_create(config, mesh) + + ! 创建解 + solver%solution = solution_create(solver%domain) + + ! 保存原始时间步长 + solver%dt_original = config%dt + + if (config%verbose) then + print *, "[SOLVER] Base solver created" + print *, " Mesh cells: ", mesh%ncells + print *, " Domain total cells: ", solver%domain%ntcells + end if + end function create_solver_base + + ! ==================== 初始化 ==================== + + subroutine solver_base_initialize(this) + class(solver_base), intent(inout) :: this + + if (this%state == SOLVER_INITIALIZED) then + if (this%config%verbose) then + print *, "[SOLVER] Already initialized" + end if + return + end if + + ! 初始化解(通过配置) + ! 这里暂时简化,实际需要调用初始条件工厂 + print *, "[INFO] Base solver initialized (simplified)" + + ! 更新状态 + this%state = SOLVER_INITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + + if (this%config%verbose) then + print *, "[SOLVER] Initialized at t = ", this%current_time + end if + end subroutine solver_base_initialize + + ! ==================== 单步计算(虚方法) ==================== + + subroutine solver_base_step(this, dt) + class(solver_base), intent(inout) :: this + real(wp), intent(in) :: dt + + ! 基类中这只是虚方法,需要在子类中实现 + print *, "[INFO] Base solver step (virtual method)" + print *, " dt = ", dt + print *, " t = ", this%current_time + + ! 更新时间 + this%current_time = this%current_time + dt + this%current_step = this%current_step + 1 + + ! 简单模拟:只是更新状态 + if (this%config%verbose) then + print *, "[SOLVER] Step completed: t = ", this%current_time, & + ", step = ", this%current_step + end if + end subroutine solver_base_step + + ! ==================== 运行到指定时间 ==================== + + subroutine solver_base_run_to_time(this, final_time) + class(solver_base), intent(inout) :: this + real(wp), intent(in) :: final_time + + real(wp) :: dt, t_remaining + integer :: step_count + + if (this%state /= SOLVER_INITIALIZED) then + this%error_message = "Solver not initialized" + this%state = SOLVER_ERROR + if (this%config%verbose) then + print *, "[SOLVER BASE ERROR] Not initialized: ", trim(this%error_message) + end if + return + end if + + this%state = SOLVER_RUNNING + step_count = 0 + + if (this%config%verbose) then + print *, "[SOLVER BASE] Running from t = ", this%current_time, & + " to t = ", final_time + print *, " Time step: ", this%config%dt + end if + + do while (this%current_time < final_time - 1e-12_wp) + ! 计算时间步长 + t_remaining = final_time - this%current_time + dt = min(this%config%dt, t_remaining) + + ! 执行时间步 + call this%step(dt) + + step_count = step_count + 1 + + ! 每50步输出一次进度 + if (mod(step_count, 50) == 0 .and. this%config%verbose) then + print *, "[SOLVER BASE] Progress: t = ", this%current_time, & + " / ", final_time, " (step ", step_count, ")" + end if + end do + + ! 恢复原始时间步长 + this%config%dt = this%dt_original + + ! 更新状态 + this%state = SOLVER_COMPLETED + + if (this%config%verbose) then + print *, "[SOLVER BASE] Run completed:" + print *, " Final time: ", this%current_time + print *, " Total steps: ", this%current_step + print *, " State: ", this%state + end if + end subroutine solver_base_run_to_time + + ! ==================== 清理 ==================== + + subroutine solver_base_cleanup(this) + class(solver_base), intent(inout) :: this + + ! 重置状态 + this%state = SOLVER_UNINITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + this%error_message = "" + + if (this%config%verbose) then + print *, "[SOLVER] Cleaned up" + end if + end subroutine solver_base_cleanup + + ! ==================== 状态查询 ==================== + + function solver_base_get_state(this) result(state) + class(solver_base), intent(in) :: this + integer :: state + state = this%state + end function solver_base_get_state + + function solver_base_get_error(this) result(error_msg) + class(solver_base), intent(in) :: this + character(len=100) :: error_msg + error_msg = trim(this%error_message) + end function solver_base_get_error + + ! ==================== 信息打印 ==================== + + subroutine solver_base_print_info(this) + class(solver_base), intent(in) :: this + + character(len=20) :: state_str + + ! 状态字符串 + select case (this%state) + case (SOLVER_UNINITIALIZED) + state_str = "Uninitialized" + case (SOLVER_INITIALIZED) + state_str = "Initialized" + case (SOLVER_RUNNING) + state_str = "Running" + case (SOLVER_COMPLETED) + state_str = "Completed" + case (SOLVER_ERROR) + state_str = "Error" + case default + state_str = "Unknown" + end select + + print *, "=== Solver Information ===" + print *, "State: ", trim(state_str) + print *, "Current time: ", this%current_time + print *, "Current step: ", this%current_step + print *, "Error message: '", trim(this%error_message), "'" + + ! 配置信息 + print *, "Configuration:" + print *, " Scheme: ", trim(this%config%recon_scheme) + print *, " Order: ", this%config%spatial_order + print *, " dt: ", this%config%dt + + ! 域信息 + print *, "Domain:" + print *, " Ghost layers: ", this%domain%nghosts + print *, " Physical cells: ", this%domain%ist, " to ", this%domain%ied - 1 + + print *, "=========================" + end subroutine solver_base_print_info + +end module solver_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/src/solver/physics_solver.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/src/solver/physics_solver.f90 new file mode 100644 index 00000000..f41ac89f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/src/solver/physics_solver.f90 @@ -0,0 +1,503 @@ +! src/solver/physics_solver.f90 (简化版) +module physics_solver_module + use base_modules, only: wp => wp, ip => ip + use config_module, only: cfd_config + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create + + use physics_interface, only: physics_equation, physics_problem + use linear_convection_equation, only: linear_convection_eq, create_linear_convection_eq + use linear_convection_problem, only: linear_convection_prob, create_linear_convection_prob + + use component_manager_module, only: create_reconstructor, create_flux_calculator + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + implicit none + private + + ! 明确导出列表 + public :: wp, ip, physics_solver, create_physics_solver + public :: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, SOLVER_RUNNING + public :: SOLVER_COMPLETED, SOLVER_ERROR + + ! 求解器状态枚举 + integer, parameter :: SOLVER_UNINITIALIZED = 0 + integer, parameter :: SOLVER_INITIALIZED = 1 + integer, parameter :: SOLVER_RUNNING = 2 + integer, parameter :: SOLVER_COMPLETED = 3 + integer, parameter :: SOLVER_ERROR = 4 + + ! 物理求解器类型(不继承,独立实现) + type :: physics_solver + ! 基本组件 + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + + ! 物理组件 + class(physics_equation), allocatable :: equation + class(physics_problem), allocatable :: problem + + ! 数值组件 + class(reconstructor_base), allocatable :: reconstructor + class(flux_calculator_base), allocatable :: flux_calculator + + ! 状态管理 + integer :: state = SOLVER_UNINITIALIZED + character(len=100) :: error_message = "" + real(wp) :: current_time = 0.0_wp + integer(ip) :: current_step = 0 + + ! 时间控制 + real(wp) :: dt_original = 0.0_wp + logical :: physics_initialized = .false. + contains + procedure :: initialize => physics_solver_initialize + procedure :: step => physics_solver_step + procedure :: run_to_time => physics_solver_run_to_time + procedure :: cleanup => physics_solver_cleanup + procedure :: get_state => physics_solver_get_state + procedure :: get_error => physics_solver_get_error + procedure :: print_info => physics_solver_print_info + procedure, private :: create_physics_components + procedure, private :: create_numerical_components + end type physics_solver + + ! 构造函数接口 + interface physics_solver + module procedure create_physics_solver + end interface + +contains + + ! ==================== 构造函数 ==================== + + function create_physics_solver(config, mesh) result(solver) + type(cfd_config), intent(in) :: config + type(mesh_type), intent(in) :: mesh + type(physics_solver) :: solver + + solver%config = config + solver%mesh = mesh + + ! 创建域 + solver%domain = domain_create(config, mesh) + + ! 创建解 + solver%solution = solution_create(solver%domain) + + ! 保存原始时间步长 + solver%dt_original = config%dt + + ! 创建组件 + call solver%create_physics_components() + call solver%create_numerical_components() + + if (config%verbose) then + print *, "[PHYSICS SOLVER] Created:" + print *, " Mesh cells: ", mesh%ncells + print *, " Domain total cells: ", solver%domain%ntcells + end if + end function create_physics_solver + + ! ==================== 创建物理组件 ==================== + + subroutine create_physics_components(this) + class(physics_solver), intent(inout) :: this + + ! 检查是否启用物理模块 + if (.not. this%config%enable_physics) then + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Physics module disabled" + end if + return + end if + + ! 创建物理方程 + select case (trim(this%config%equation_type)) + case ("linear_advection") + allocate(linear_convection_eq :: this%equation) + select type(eq => this%equation) + type is(linear_convection_eq) + eq = create_linear_convection_eq(wave_speed=this%config%wave_speed) + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Created linear convection equation" + print *, " Wave speed: ", eq%wave_speed + end if + end select + + case default + if (this%config%verbose) then + print *, "[WARNING] Unknown equation type: ", trim(this%config%equation_type) + print *, " Using linear convection as default" + end if + + allocate(linear_convection_eq :: this%equation) + select type(eq => this%equation) + type is(linear_convection_eq) + eq = create_linear_convection_eq(wave_speed=this%config%wave_speed) + end select + end select + + ! 创建物理问题 + select case (trim(this%config%problem_type)) + case ("linear_advection") + allocate(linear_convection_prob :: this%problem) + select type(prob => this%problem) + type is(linear_convection_prob) + prob = create_linear_convection_prob( & + wave_speed=this%config%wave_speed, & + domain_length=this%config%domain_length, & + ic_type=this%config%ic_type, & + boundary_type=this%config%boundary_type) + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Created linear convection problem" + print *, " IC type: ", trim(prob%ic_type) + end if + end select + + case default + if (this%config%verbose) then + print *, "[WARNING] Unknown problem type: ", trim(this%config%problem_type) + print *, " Using linear convection as default" + end if + + allocate(linear_convection_prob :: this%problem) + select type(prob => this%problem) + type is(linear_convection_prob) + prob = create_linear_convection_prob( & + wave_speed=this%config%wave_speed, & + domain_length=this%config%domain_length, & + ic_type=this%config%ic_type, & + boundary_type=this%config%boundary_type) + end select + end select + + this%physics_initialized = .true. + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Physics components created" + end if + end subroutine create_physics_components + + ! ==================== 创建数值组件 ==================== + + subroutine create_numerical_components(this) + class(physics_solver), intent(inout) :: this + integer :: status + + ! 创建重构器 + this%reconstructor = create_reconstructor(this%config, status) + if (status /= 0) then + print *, "[ERROR] Failed to create reconstructor" + this%state = SOLVER_ERROR + this%error_message = "Failed to create reconstructor" + return + end if + + ! 创建通量计算器 + this%flux_calculator = create_flux_calculator(this%config, status) + if (status /= 0) then + print *, "[ERROR] Failed to create flux calculator" + this%state = SOLVER_ERROR + this%error_message = "Failed to create flux calculator" + return + end if + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Numerical components created" + end if + end subroutine create_numerical_components + + ! ==================== 初始化 ==================== + + subroutine physics_solver_initialize(this) + class(physics_solver), intent(inout) :: this + + if (this%state == SOLVER_INITIALIZED) then + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Already initialized" + end if + return + end if + + ! 如果启用了物理模块,应用初始条件 + if (this%physics_initialized .and. allocated(this%problem)) then + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Applying initial condition" + end if + + select type(prob => this%problem) + type is(linear_convection_prob) + ! 获取网格单元中心坐标 + call prob%initial_condition(this%mesh%xcc, & + this%solution%u(this%domain%ist:this%domain%ied-1)) + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Initial condition applied" + end if + end select + else + ! 简化的初始化:阶跃函数 + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Using simplified initialization" + end if + + ! 在 [0.5, 1.0] 区域内设为 2.0,其他区域为 1.0 + where (this%mesh%xcc >= 0.5_wp .and. this%mesh%xcc <= 1.0_wp) + this%solution%u(this%domain%ist:this%domain%ied-1) = 2.0_wp + elsewhere + this%solution%u(this%domain%ist:this%domain%ied-1) = 1.0_wp + end where + end if + + ! 同步旧场 + call this%solution%update_old_field() + + ! 更新状态 + this%state = SOLVER_INITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Initialized at t = ", this%current_time + end if + end subroutine physics_solver_initialize + + ! ==================== 时间步进 ==================== + + subroutine physics_solver_step(this, dt) + class(physics_solver), intent(inout) :: this + real(wp), intent(in) :: dt + + integer :: i + real(wp) :: u_val, f_val + + if (this%config%verbose .and. mod(this%current_step, 100) == 0) then + print *, "[PHYSICS SOLVER] Step ", this%current_step + 1, & + " dt = ", dt, " t = ", this%current_time + end if + + ! 更新旧场 + call this%solution%update_old_field() + + ! 简化的数值方法 + do i = this%domain%ist, this%domain%ied - 1 + u_val = this%solution%un(i) ! 使用旧值 + + ! 简单的线性对流:u_t + a*u_x = 0 + ! 使用一阶迎风格式 + this%solution%u(i) = u_val - dt * this%config%wave_speed * & + (u_val - this%solution%un(i-1)) / this%mesh%dx + end do + + ! 更新时间 + this%current_time = this%current_time + dt + this%current_step = this%current_step + 1 + + ! 每100步输出一次进度 + if (this%config%verbose .and. mod(this%current_step, 100) == 0) then + print *, "[PHYSICS SOLVER] Step ", this%current_step, & + " completed, t = ", this%current_time + end if + end subroutine physics_solver_step + + ! ==================== 运行到指定时间 ==================== + + subroutine physics_solver_run_to_time(this, final_time) + class(physics_solver), intent(inout) :: this + real(wp), intent(in) :: final_time + + real(wp) :: dt, t_remaining + integer :: step_count + + if (this%state /= SOLVER_INITIALIZED) then + this%error_message = "Solver not initialized" + this%state = SOLVER_ERROR + if (this%config%verbose) then + print *, "[PHYSICS SOLVER ERROR] Not initialized" + end if + return + end if + + this%state = SOLVER_RUNNING + step_count = 0 + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Running from t = ", this%current_time, & + " to t = ", final_time + end if + + do while (this%current_time < final_time - 1e-12_wp) + ! 计算时间步长 + t_remaining = final_time - this%current_time + dt = min(this%config%dt, t_remaining) + + ! 执行时间步 + call this%step(dt) + + step_count = step_count + 1 + + ! 每100步输出一次进度 + if (mod(step_count, 100) == 0 .and. this%config%verbose) then + print *, "[PHYSICS SOLVER] Progress: t = ", this%current_time, & + " / ", final_time + end if + end do + + ! 恢复原始时间步长 + this%config%dt = this%dt_original + + ! 更新状态 + this%state = SOLVER_COMPLETED + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Run completed:" + print *, " Final time: ", this%current_time + print *, " Total steps: ", this%current_step + print *, " Final u range: ", minval(this%solution%u), " to ", maxval(this%solution%u) + end if + end subroutine physics_solver_run_to_time + + ! ==================== 清理 ==================== + + subroutine physics_solver_cleanup(this) + class(physics_solver), intent(inout) :: this + + integer :: old_state + real(wp) :: old_time + integer(ip) :: old_step + + ! 保存清理前的状态用于调试 + old_state = this%state + old_time = this%current_time + old_step = this%current_step + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Cleaning up..." + print *, " Before cleanup - State: ", old_state + print *, " Before cleanup - Time: ", old_time + print *, " Before cleanup - Steps: ", old_step + end if + + ! 清理物理组件 + if (allocated(this%equation)) then + deallocate(this%equation) + if (this%config%verbose) then + print *, " Deallocated equation" + end if + end if + + if (allocated(this%problem)) then + deallocate(this%problem) + if (this%config%verbose) then + print *, " Deallocated problem" + end if + end if + + ! 清理数值组件 + if (allocated(this%reconstructor)) then + deallocate(this%reconstructor) + if (this%config%verbose) then + print *, " Deallocated reconstructor" + end if + end if + + if (allocated(this%flux_calculator)) then + deallocate(this%flux_calculator) + if (this%config%verbose) then + print *, " Deallocated flux calculator" + end if + end if + + ! 重置状态 - 但不重置解数组(保持分配) + this%state = SOLVER_UNINITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + this%error_message = "" + this%physics_initialized = .false. + + if (this%config%verbose) then + print *, " After cleanup - State: ", this%state + print *, " After cleanup - Time: ", this%current_time + print *, " After cleanup - Steps: ", this%current_step + print *, "[PHYSICS SOLVER] Cleanup completed" + end if + + end subroutine physics_solver_cleanup + + ! ==================== 状态查询 ==================== + + function physics_solver_get_state(this) result(state) + class(physics_solver), intent(in) :: this + integer :: state + state = this%state + end function physics_solver_get_state + + function physics_solver_get_error(this) result(error_msg) + class(physics_solver), intent(in) :: this + character(len=100) :: error_msg + error_msg = trim(this%error_message) + end function physics_solver_get_error + + ! ==================== 信息打印 ==================== + + subroutine physics_solver_print_info(this) + class(physics_solver), intent(in) :: this + + character(len=20) :: state_str + + ! 状态字符串 + select case (this%state) + case (SOLVER_UNINITIALIZED) + state_str = "Uninitialized" + case (SOLVER_INITIALIZED) + state_str = "Initialized" + case (SOLVER_RUNNING) + state_str = "Running" + case (SOLVER_COMPLETED) + state_str = "Completed" + case (SOLVER_ERROR) + state_str = "Error" + case default + write(state_str, '(A, I3)') "Unknown ", this%state + end select + + print *, "=== Physics Solver Information ===" + print *, "State: ", trim(state_str), " (", this%state, ")" + print *, "Current time: ", this%current_time + print *, "Current step: ", this%current_step + print *, "Error message: '", trim(this%error_message), "'" + + ! 配置信息 + print *, "Configuration:" + print *, " Scheme: ", trim(this%config%recon_scheme) + print *, " Order: ", this%config%spatial_order + print *, " dt: ", this%config%dt + print *, " Final time: ", this%config%final_time + + ! 域信息 + print *, "Domain:" + print *, " Ghost layers: ", this%domain%nghosts + print *, " Physical cells: ", this%domain%ist, " to ", this%domain%ied - 1 + + ! 物理信息 + print *, "Physics:" + print *, " Initialized: ", this%physics_initialized + print *, " Equation type: ", trim(this%config%equation_type) + print *, " Problem type: ", trim(this%config%problem_type) + + ! 解信息 + if (allocated(this%solution%u)) then + print *, "Solution:" + print *, " u size: ", size(this%solution%u) + print *, " u physical size: ", this%domain%ied - this%domain%ist + end if + + print *, "===================================" + end subroutine physics_solver_print_info + +end module physics_solver_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/CMakeLists.txt new file mode 100644 index 00000000..b609e28d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/CMakeLists.txt @@ -0,0 +1,106 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +# 基础设施测试 +add_executable(test_infrastructure test_infrastructure.f90) +target_link_libraries(test_infrastructure + PRIVATE + infrastructure + core +) + +# 注册系统测试 +add_executable(test_registry test_registry.f90) +target_link_libraries(test_registry + PRIVATE + core + infrastructure +) + +# 物理模块测试 +add_executable(test_physics test_physics.f90) +target_link_libraries(test_physics + PRIVATE + physics + base +) + +# 组件管理器测试 +add_executable(test_component_manager test_component_manager.f90) +target_link_libraries(test_component_manager + PRIVATE + manager + infrastructure +) + +# 配置物理测试 +add_executable(test_config_physics test_config_physics.f90) +target_link_libraries(test_config_physics + PRIVATE + infrastructure + core +) + +# 求解器基础测试 +add_executable(test_solver_base test_solver_base.f90) +target_link_libraries(test_solver_base + PRIVATE + solver + infrastructure + core +) + +# 物理求解器测试 +add_executable(test_physics_solver test_physics_solver.f90) +target_link_libraries(test_physics_solver + PRIVATE + solver + infrastructure + core + physics + manager +) + +# 新增:简单物理求解器测试 +add_executable(test_physics_solver_simple test_physics_solver_simple.f90) +target_link_libraries(test_physics_solver_simple + PRIVATE + solver + infrastructure + core + physics + manager +) + +add_executable(test_simple_link test_simple_link.f90) +target_link_libraries(test_simple_link + PRIVATE + reconstructor + flux +) + + +add_executable(test_factory_simple test_factory_simple.f90) +target_link_libraries(test_factory_simple + PRIVATE + core + infrastructure + reconstructor + flux +) + +add_executable(test_domain_solution test_domain_solution.f90) +target_link_libraries(test_domain_solution + PRIVATE + infrastructure + core +) + +add_executable(test_component_manager_physics test_component_manager_physics.f90) +target_link_libraries(test_component_manager_physics + PRIVATE + manager + infrastructure + physics + core +) diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_cfd_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_cfd_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_cfd_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_component_manager.f90 new file mode 100644 index 00000000..f60c3505 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_component_manager.f90 @@ -0,0 +1,111 @@ +! tests/test_component_manager.f90 +program test_component_manager + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use component_manager_module, only: create_reconstructor, create_flux_calculator + use component_manager_module, only: component_manager_info, validate_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + implicit none + + type(cfd_config) :: config + class(reconstructor_base), allocatable :: recon + class(flux_calculator_base), allocatable :: flux + integer :: status + logical :: is_valid + + print *, "=== Component Manager Test ===" + print *, "" + + ! 显示组件管理器信息 + call component_manager_info() + print *, "" + + ! 测试1: 基本配置 + print *, "1. Testing basic ENO3 + Rusanov configuration..." + print *, "-----------------------------------------------" + + config%verbose = .true. + call config_print(config) + + ! 配置ENO3重构 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + call config_print(config) + print *, "" + + ! 验证配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Configuration is valid" + else + print *, "[ERROR] Configuration is invalid" + end if + print *, "" + + ! 测试2: 创建组件 + print *, "2. Testing component creation..." + print *, "--------------------------------" + + ! 创建重构器(带状态检查) + recon = create_reconstructor(config, status) + if (status == 0) then + print *, "[OK] Reconstructor created successfully" + call recon%info() + else + print *, "[ERROR] Failed to create reconstructor, code:", status + end if + print *, "" + + ! 创建通量计算器 + flux = create_flux_calculator(config, status) + if (status == 0) then + print *, "[OK] Flux calculator created successfully" + call flux%info() + else + print *, "[ERROR] Failed to create flux calculator, code:", status + end if + print *, "" + + ! 测试3: WENO3重构测试 + print *, "3. Testing WENO3 configuration..." + print *, "---------------------------------" + + call config_with_reconstruction(config, "weno3", 3) + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] WENO3 configuration is valid" + + ! 创建WENO3重构器 + recon = create_reconstructor(config) + call recon%info() + else + print *, "[ERROR] WENO3 configuration is invalid" + end if + print *, "" + + ! 测试4: 错误配置测试 + print *, "4. Testing invalid configuration..." + print *, "-----------------------------------" + + config%recon_scheme = "unknown_scheme" + config%flux_type = "unknown_flux" + + is_valid = validate_config(config) + if (.not. is_valid) then + print *, "[OK] Invalid configuration correctly rejected" + else + print *, "[ERROR] Invalid configuration should have been rejected" + end if + + ! 清理 + if (allocated(recon)) deallocate(recon) + if (allocated(flux)) deallocate(flux) + + print *, "" + print *, "=== Component manager test completed successfully ===" + +end program test_component_manager \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_component_manager_physics.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_component_manager_physics.f90 new file mode 100644 index 00000000..f2becca9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_component_manager_physics.f90 @@ -0,0 +1,120 @@ +! tests/test_component_manager_physics.f90 (简化版) +program test_component_manager_physics + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + use component_manager_module, only: component_manager_info, validate_config + + implicit none + + type(cfd_config) :: config + logical :: is_valid + + print *, "=== Component Manager Physics Test (Simplified) ===" + print *, "" + + ! 测试1: 显示组件管理器信息 + print *, "1. Testing component manager info..." + print *, "-------------------------------------" + call component_manager_info() + print *, "" + + ! 测试2: 物理模块测试(默认) + print *, "2. Testing physics module with default configuration..." + print *, "------------------------------------------------------" + + config%verbose = .true. + call config_print(config) + + ! 验证配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Default configuration is valid" + else + print *, "[ERROR] Default configuration is invalid" + end if + print *, "" + + ! 测试3: 测试物理配置 + print *, "3. Testing physics configuration..." + print *, "------------------------------------" + + ! 修改物理参数 + config%equation_type = "linear_advection" + config%problem_type = "linear_advection" + config%wave_speed = 2.5_wp + config%domain_length = 3.0_wp + + print *, "Modified physics configuration:" + print *, " Equation type: ", trim(config%equation_type) + print *, " Problem type: ", trim(config%problem_type) + print *, " Wave speed: ", config%wave_speed + print *, " Domain length: ", config%domain_length + print *, " Physics enabled: ", config%enable_physics + + ! 验证修改后的配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Modified physics configuration is valid" + else + print *, "[ERROR] Modified physics configuration is invalid" + end if + print *, "" + + ! 测试4: 数值组件测试 + print *, "4. Testing numerical components with physics..." + print *, "-----------------------------------------------" + + call config_with_reconstruction(config, "weno3", 3) + config%flux_type = "rusanov" + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Combined physics+numerics configuration is valid" + else + print *, "[ERROR] Combined configuration is invalid" + end if + print *, "" + + ! 测试5: 物理模块禁用测试 + print *, "5. Testing physics module disabled..." + print *, "---------------------------------------" + + config%enable_physics = .false. + config%verbose = .false. + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Configuration valid even with physics disabled" + else + print *, "[ERROR] Configuration should be valid with physics disabled" + end if + print *, "" + + ! 测试6: 错误配置测试 + print *, "6. Testing error handling..." + print *, "-----------------------------" + + config%verbose = .true. + config%enable_physics = .true. + config%recon_scheme = "unknown_scheme" + config%flux_type = "unknown_flux" + config%equation_type = "unknown_equation" + config%problem_type = "unknown_problem" + + is_valid = validate_config(config) + if (.not. is_valid) then + print *, "[OK] Invalid configuration correctly rejected" + else + print *, "[ERROR] Invalid configuration should have been rejected" + end if + print *, "" + + print *, "=== Component Manager Physics Test Summary ===" + print *, "✓ Component manager info works" + print *, "✓ Configuration validation works with physics" + print *, "✓ Error handling works correctly" + print *, "✓ Combined physics+numerics validation works" + print *, "" + print *, "下一步: 集成物理模块到求解器框架" + +end program test_component_manager_physics \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_config_physics.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_config_physics.f90 new file mode 100644 index 00000000..c6fef5c0 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_config_physics.f90 @@ -0,0 +1,141 @@ +! tests/test_config_physics.f90 (修复版) +program test_config_physics + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + + implicit none + + type(cfd_config) :: config + + print *, "=== Configuration Physics Test (Simplified) ===" + print *, "" + + ! 测试1: 默认配置 + print *, "1. Testing default configuration..." + print *, "-----------------------------------" + call config_print(config) + print *, "" + + ! 测试2: 验证基础物理字段 + print *, "2. Testing basic physics fields..." + print *, "----------------------------------" + + print *, "Verifying default physics fields:" + + if (trim(config%equation_type) == "linear_advection") then + print *, " ✓ Default equation type: linear_advection" + else + print *, " ✗ Unexpected equation type: ", trim(config%equation_type) + end if + + if (trim(config%problem_type) == "linear_advection") then + print *, " ✓ Default problem type: linear_advection" + else + print *, " ✗ Unexpected problem type: ", trim(config%problem_type) + end if + + if (abs(config%domain_length - 2.0_wp) < 1e-10_wp) then + print *, " ✓ Default domain length: 2.0" + else + print *, " ✗ Unexpected domain length: ", config%domain_length + end if + + if (config%enable_physics) then + print *, " ✓ Physics enabled by default" + else + print *, " ✗ Physics not enabled by default" + end if + + print *, "" + + ! 测试3: 使用类型绑定的方法(正确的方法名) + print *, "3. Testing type-bound procedures..." + print *, "--------------------------------------" + + call config%set_physics_parameters( & + equation_type="burgers_equation", & + problem_type="sod_shock_tube", & + domain_length=3.0_wp, & + enable_physics=.false.) + + print *, "After set_physics_parameters:" + print *, " Equation type: ", trim(config%equation_type) + print *, " Problem type: ", trim(config%problem_type) + print *, " Domain length: ", config%domain_length + print *, " Physics enabled: ", config%enable_physics + + if (trim(config%equation_type) == "burgers_equation") then + print *, " ✓ Equation type modified successfully via set_physics_parameters" + end if + + if (trim(config%problem_type) == "sod_shock_tube") then + print *, " ✓ Problem type modified successfully via set_physics_parameters" + end if + + if (abs(config%domain_length - 3.0_wp) < 1e-10_wp) then + print *, " ✓ Domain length modified successfully via set_physics_parameters" + end if + + if (.not. config%enable_physics) then + print *, " ✓ Physics disabled successfully via set_physics_parameters" + end if + + print *, "" + + ! 测试4: 调用get_physics_info方法 + print *, "4. Testing get_physics_info method..." + print *, "--------------------------------------" + call config%get_physics_info() + print *, "" + + ! 测试5: 高斯脉冲配置 + print *, "5. Testing Gaussian pulse configuration..." + print *, "-----------------------------------------" + + config%ic_type = "gaussian" + config%pulse_center = 0.6_wp + config%pulse_width = 0.15_wp + + print *, "Gaussian pulse parameters:" + print *, " IC type: ", trim(config%ic_type) + print *, " Center: ", config%pulse_center + print *, " Width: ", config%pulse_width + + if (trim(config%ic_type) == "gaussian") then + print *, " ✓ Gaussian IC type set" + end if + + if (abs(config%pulse_center - 0.6_wp) < 1e-10_wp) then + print *, " ✓ Pulse center set" + end if + + if (abs(config%pulse_width - 0.15_wp) < 1e-10_wp) then + print *, " ✓ Pulse width set" + end if + + print *, "" + + ! 测试6: 重构配置 + print *, "6. Testing reconstruction configuration..." + print *, "------------------------------------------" + + call config_with_reconstruction(config, "weno", 5) + + print *, "Reconstruction configuration:" + print *, " Scheme: ", trim(config%recon_scheme) + print *, " Order: ", config%spatial_order + + if (trim(config%recon_scheme) == "weno" .and. config%spatial_order == 5) then + print *, " ✓ WENO5 configuration successful" + else + print *, " ✗ Reconstruction configuration failed" + end if + + print *, "" + + print *, "=== Configuration Physics Test Complete ===" + print *, "✓ Config module updated with physics support" + print *, "✓ Fields can be directly accessed and modified" + print *, "✓ Type-bound procedures work correctly" + +end program test_config_physics \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_domain_solution.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_domain_solution.f90 new file mode 100644 index 00000000..ff659bac --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_domain_solution.f90 @@ -0,0 +1,102 @@ +! tests/test_domain_solution.f90 +program test_domain_solution + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create, solution_reset + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + real(wp), allocatable :: initial_values(:) + integer :: i + + print *, "=== Domain and Solution Test ===" + print *, "" + + ! 测试1: 不同重构方案的ghost层计算 + print *, "1. Testing ghost layer calculation..." + print *, "--------------------------------------" + + ! ENO3 + call config_with_reconstruction(config, "eno", 3) + config%verbose = .false. + call mesh%init(ncells=10) + domain = domain_create(config, mesh) + print *, "ENO3: nghosts = ", domain%nghosts, " (expected: 3)" + + ! WENO3 + call config_with_reconstruction(config, "weno3", 3) + domain = domain_create(config, mesh) + print *, "WENO3: nghosts = ", domain%nghosts, " (expected: 2)" + + ! WENO5 + call config_with_reconstruction(config, "weno", 5) + domain = domain_create(config, mesh) + print *, "WENO5: nghosts = ", domain%nghosts, " (expected: 3)" + print *, "" + + ! 测试2: Solution数组 + print *, "2. Testing solution arrays..." + print *, "------------------------------" + + call config_with_reconstruction(config, "eno", 3) + config%verbose = .true. + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + domain = domain_create(config, mesh) + call domain%print_info() + print *, "" + + solution = solution_create(domain) + call solution%print_info() + print *, "" + + ! 测试3: 初始化和更新 + print *, "3. Testing initialization and update..." + print *, "----------------------------------------" + + allocate(initial_values(mesh%ncells)) + do i = 1, mesh%ncells + initial_values(i) = sin(2.0_wp * 3.14159265358979_wp * mesh%xcc(i) / mesh%L) + end do + + call solution%initialize(initial_values) + print *, "After initialization:" + print *, " u range: ", minval(solution%u), " to ", maxval(solution%u) + print *, " un range: ", minval(solution%un), " to ", maxval(solution%un) + + ! 修改当前解,测试更新 + solution%u = solution%u * 2.0_wp + call solution%update_old_field() + print *, "After update: max|u - un| = ", maxval(abs(solution%u - solution%un)) + print *, "" + + ! 测试4: 重置 + print *, "4. Testing reset..." + print *, "-------------------" + + call solution_reset(solution) + print *, "After reset:" + print *, " u max: ", maxval(abs(solution%u)) + print *, " un max: ", maxval(abs(solution%un)) + print *, " flux max: ", maxval(abs(solution%flux)) + print *, "" + + deallocate(initial_values) + + print *, "=== Test Summary ===" + print *, "✓ Ghost layer calculation works" + print *, "✓ Domain creation works" + print *, "✓ Solution arrays work" + print *, "✓ Initialization works" + print *, "✓ Field update works" + print *, "✓ Reset works" + print *, "" + print *, "Ready for next step: Implementing Physics modules" + +end program test_domain_solution \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_factory_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_factory_simple.f90 new file mode 100644 index 00000000..db65da7c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_factory_simple.f90 @@ -0,0 +1,58 @@ +! tests/test_factory_simple.f90 (修复版) +program test_factory_simple + use base_modules, only: wp ! ← 添加这行 + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Basic systems + print *, "1. Testing basic systems..." + print *, "-----------------------------" + call config_print(config) + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Creating reconstructors + print *, "2. Testing reconstructors..." + print *, "------------------------------" + + ! 创建并测试ENO重构器 + print *, "Creating ENO reconstructor..." + eno = eno_reconstructor() ! 使用构造函数 + call eno%info() ! 必须调用info方法 + + print *, "" + print *, "Creating WENO3 reconstructor..." + weno3 = weno3_reconstructor() ! 使用构造函数 + call weno3%info() ! 必须调用info方法 + print *, "" + + ! Test 3: Creating flux calculator + print *, "3. Testing flux calculator..." + print *, "-------------------------------" + + print *, "Creating Rusanov flux calculator..." + rusanov = rusanov_flux() ! 使用构造函数 + call rusanov%info() ! 必须调用info方法 + print *, "" + + print *, "=== Factory pattern simple test completed successfully ===" + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_infrastructure.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_infrastructure.f90 new file mode 100644 index 00000000..22fa92d1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_infrastructure.f90 @@ -0,0 +1,56 @@ +! tests/test_infrastructure.f90 (原test_basic_only.f90) +program test_infrastructure + use base_modules, only: wp + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use registry_module, only: registry_init, registry_cleanup, & + register_component_simple, list_components + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "=== 基础设施测试 ===" + print *, "" + + ! 测试1: 配置 + print *, "1. 测试配置模块..." + print *, "-------------------" + call config_print(config) + print *, "" + + ! 测试2: 网格 + print *, "2. 测试网格模块..." + print *, "------------------" + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=5) + print *, "网格初始化:" + print *, " 单元数: ", mesh%ncells + print *, " 节点数: ", mesh%nnodes + print *, " 网格间距: ", mesh%dx + print *, "" + + ! 测试3: 注册系统 + print *, "3. 测试注册系统..." + print *, "------------------" + + call registry_init() + + ! 注册组件(使用简化版本) + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! 列出组件 + call list_components() + print *, "" + + ! 清理 + call registry_cleanup() + + print *, "=== 基础设施测试通过 ===" + print *, "✓ 配置模块工作正常" + print *, "✓ 网格模块工作正常" + print *, "✓ 注册系统工作正常" + +end program test_infrastructure \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_physics.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_physics.f90 new file mode 100644 index 00000000..3dababb6 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_physics.f90 @@ -0,0 +1,91 @@ +! tests/test_physics.f90 (原test_physics_minimal.f90) +program test_physics + use base_modules, only: wp, ip + use linear_convection_equation, only: linear_convection_eq, create_linear_convection_eq + use linear_convection_problem, only: linear_convection_prob, create_linear_convection_prob + + implicit none + + type(linear_convection_eq) :: eq + type(linear_convection_prob) :: prob + real(wp) :: u, f, a + real(wp), allocatable :: x(:), u_ic(:), u_exact(:) + integer :: i, nx = 10 + + print *, "=== 物理模块基础测试 ===" + print *, "" + + ! 测试1: 方程功能 + print *, "1. 测试方程功能..." + print *, "-------------------" + + eq = create_linear_convection_eq(wave_speed=2.0_wp) + print *, "方程: ", eq%name + print *, "波速: ", eq%wave_speed + + u = 1.5_wp + f = eq%flux(u) + a = eq%speed() + + print *, "u = ", u + print *, "F(u) = ", f, " (期望: 3.0)" + print *, "波速 a = ", a, " (期望: 2.0)" + + if (abs(f - 3.0_wp) < 1e-10_wp .and. abs(a - 2.0_wp) < 1e-10_wp) then + print *, "✓ 方程功能正常" + else + print *, "✗ 方程功能异常" + end if + print *, "" + + ! 测试2: 问题功能 + print *, "2. 测试问题功能..." + print *, "-------------------" + + prob = create_linear_convection_prob(ic_type="step", domain_length=2.0_wp) + print *, "问题: ", prob%name + print *, "初始条件类型: ", trim(prob%ic_type) + print *, "域长度: ", prob%domain_length + + allocate(x(nx), u_ic(nx), u_exact(nx)) + do i = 1, nx + x(i) = 0.0_wp + (i-1) * 0.2_wp + end do + + ! 测试初始条件 + call prob%initial_condition(x, u_ic) + print *, "初始条件范围: ", minval(u_ic), " 到 ", maxval(u_ic) + + ! 测试精确解 + u_exact = prob%exact_solution(x, 0.0_wp) + print *, "t=0时精确解范围: ", minval(u_exact), " 到 ", maxval(u_exact) + + ! 检查阶跃函数 + if (abs(u_ic(1) - 1.0_wp) < 1e-10_wp .and. & + abs(u_ic(6) - 2.0_wp) < 1e-10_wp) then + print *, "✓ 阶跃初始条件正确" + else + print *, "✗ 阶跃初始条件错误" + end if + + ! 检查精确解与初始条件一致 + if (maxval(abs(u_ic - u_exact)) < 1e-10_wp) then + print *, "✓ t=0时精确解与初始条件一致" + else + print *, "✗ 精确解计算错误" + end if + print *, "" + + ! 测试3: 边界条件接口 + print *, "3. 测试边界条件接口..." + print *, "----------------------" + call prob%boundary_condition(u_ic, 0.0_wp) + print *, "✓ 边界条件接口正常" + print *, "" + + deallocate(x, u_ic, u_exact) + + print *, "=== 物理模块测试完成 ===" + print *, "下一步: 将物理模块集成到现有系统中" + +end program test_physics \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_physics_solver.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_physics_solver.f90 new file mode 100644 index 00000000..ba49ffba --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_physics_solver.f90 @@ -0,0 +1,133 @@ +! tests/test_physics_solver.f90 (修复版) +program test_physics_solver + use base_modules, only: wp ! 使用一致的wp定义 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use mesh_module, only: mesh_type + use solver_base_module, only: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, & + SOLVER_COMPLETED, SOLVER_ERROR + use physics_solver_module, only: physics_solver + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(physics_solver) :: psolver + character(len=100) :: error_msg + integer :: state + + print *, "=== Physics Solver Test ===" + print *, "" + + ! 测试1: 创建物理求解器(默认物理配置) + print *, "1. Creating physics solver (default physics)..." + print *, "------------------------------------------------" + + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%dt = 0.01_wp + config%enable_physics = .true. + + call config_print(config) + + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + psolver = physics_solver(config, mesh) + call psolver%print_info() + print *, "" + + ! 测试2: 初始化 + print *, "2. Initializing physics solver..." + print *, "----------------------------------" + + call psolver%initialize() + state = psolver%get_state() + error_msg = psolver%get_error() + print *, "State after initialization: ", state + print *, "Expected: ", SOLVER_INITIALIZED + print *, "Match? ", state == SOLVER_INITIALIZED + print *, "Error message: '", trim(error_msg), "'" + print *, "" + + ! 测试3: 运行一小段时间 + print *, "3. Running physics solver (short time)..." + print *, "------------------------------------------" + + call psolver%run_to_time(0.02_wp) + state = psolver%get_state() + error_msg = psolver%get_error() + print *, "State after short run: ", state + print *, "Expected: ", SOLVER_COMPLETED + print *, "Match? ", state == SOLVER_COMPLETED + print *, "Current time: ", psolver%current_time + print *, "Current step: ", psolver%current_step + print *, "" + + ! 测试4: 禁用物理模块 + print *, "4. Testing physics solver with physics disabled..." + print *, "--------------------------------------------------" + + config%enable_physics = .false. + config%verbose = .false. + + psolver = physics_solver(config, mesh) + call psolver%initialize() + call psolver%run_to_time(0.01_wp) + + state = psolver%get_state() + error_msg = psolver%get_error() + print *, "State with physics disabled: ", state + print *, "Expected: ", SOLVER_COMPLETED + print *, "Match? ", state == SOLVER_COMPLETED + print *, "" + + ! 测试5: 不同物理配置 + print *, "5. Testing different physics configurations..." + print *, "----------------------------------------------" + + config%verbose = .true. + config%enable_physics = .true. + config%equation_type = "linear_advection" + config%problem_type = "linear_advection" + config%wave_speed = 2.5_wp + config%domain_length = 3.0_wp + config%ic_type = "gaussian" + + psolver = physics_solver(config, mesh) + call psolver%initialize() + + print *, "Physics configuration test completed" + print *, "" + + ! 测试6: 清理和错误处理 + print *, "6. Testing cleanup and error handling..." + print *, "----------------------------------------" + + call psolver%cleanup() + state = psolver%get_state() + error_msg = psolver%get_error() + print *, "State after cleanup: ", state + print *, "Expected: ", SOLVER_UNINITIALIZED + print *, "Match? ", state == SOLVER_UNINITIALIZED + + ! 尝试运行已清理的求解器 + call psolver%run_to_time(0.01_wp) + state = psolver%get_state() + error_msg = psolver%get_error() + print *, "State after error attempt: ", state + print *, "Expected: ", SOLVER_ERROR + print *, "Match? ", state == SOLVER_ERROR + print *, "Error message: '", trim(error_msg), "'" + print *, "" + + ! 最终信息 + print *, "=== Physics Solver Test Complete ===" + print *, "✓ Physics solver creation works" + print *, "✓ Physics component initialization works" + print *, "✓ Physics-enabled time stepping works" + print *, "✓ Physics disabled mode works" + print *, "✓ Different physics configurations work" + print *, "✓ Cleanup and error handling work" + print *, "" + print *, "下一步: 实现完整的数值方法(重构、通量、时间积分)" + +end program test_physics_solver \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_physics_solver_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_physics_solver_simple.f90 new file mode 100644 index 00000000..13312efd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_physics_solver_simple.f90 @@ -0,0 +1,161 @@ +! tests/test_physics_solver_simple.f90 +program test_physics_solver_simple + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction + use mesh_module, only: mesh_type + use physics_solver_module, only: physics_solver, SOLVER_COMPLETED + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(physics_solver) :: solver + real(wp) :: final_time, final_step + integer :: state + + print *, "=========================================" + print *, " 简单物理求解器测试" + print *, "=========================================" + print *, "" + + ! 步骤1: 配置 + print *, "[步骤1] 配置求解器..." + print *, "---------------------" + + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%dt = 0.01_wp + config%final_time = 0.1_wp + config%wave_speed = 1.0_wp + config%ic_type = "step" + config%boundary_type = "periodic" + config%equation_type = "linear_advection" + config%problem_type = "linear_advection" + config%enable_physics = .true. + config%domain_length = 1.0_wp + + print *, "配置参数:" + print *, " 重构格式: ", trim(config%recon_scheme) + print *, " 时间步长: ", config%dt + print *, " 最终时间: ", config%final_time + print *, " 波速: ", config%wave_speed + print *, "" + + ! 步骤2: 创建网格 + print *, "[步骤2] 创建网格..." + print *, "-------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + + print *, "网格信息:" + print *, " 单元数: ", mesh%ncells + print *, " 节点数: ", mesh%nnodes + print *, " 网格间距: ", mesh%dx + print *, "" + + ! 步骤3: 创建求解器 + print *, "[步骤3] 创建求解器..." + print *, "---------------------" + + solver = physics_solver(config, mesh) + + print *, "求解器创建成功" + print *, " 初始状态: ", solver%get_state() + print *, "" + + ! 步骤4: 初始化 + print *, "[步骤4] 初始化求解器..." + print *, "-----------------------" + + call solver%initialize() + + state = solver%get_state() + print *, "初始化完成" + print *, " 状态: ", state + print *, " 当前时间: ", solver%current_time + print *, " 当前步数: ", solver%current_step + print *, "" + + ! 步骤5: 运行求解器 + print *, "[步骤5] 运行求解器..." + print *, "---------------------" + + call solver%run_to_time(config%final_time) + + state = solver%get_state() + print *, "运行完成" + print *, " 状态: ", state + print *, " 最终时间: ", solver%current_time + print *, " 总步数: ", solver%current_step + print *, "" + + ! 步骤6: 保存结果 + print *, "[步骤6] 保存结果..." + print *, "-------------------" + + final_time = solver%current_time + final_step = real(solver%current_step, wp) + state = solver%get_state() + + print *, "保存的结果:" + print *, " 状态: ", state + print *, " 时间: ", final_time + print *, " 步数: ", final_step + print *, "" + + ! 步骤7: 清理求解器 + print *, "[步骤7] 清理求解器..." + print *, "---------------------" + + call solver%cleanup() + + print *, "清理后状态:" + print *, " 状态: ", solver%get_state() + print *, " 时间: ", solver%current_time + print *, " 步数: ", solver%current_step + print *, "" + + ! 步骤8: 验证结果 + print *, "[步骤8] 验证结果..." + print *, "-------------------" + + print *, "验证标准:" + print *, " 1. 运行后状态应为 COMPLETED (", SOLVER_COMPLETED, ")" + print *, " 2. 最终时间应接近 ", config%final_time + print *, " 3. 步数应大于 0" + print *, "" + + if (state == SOLVER_COMPLETED) then + print *, "✓ 状态验证通过: COMPLETED" + else + print *, "✗ 状态验证失败: 期望 ", SOLVER_COMPLETED, ", 实际 ", state + end if + + if (abs(final_time - config%final_time) < 1e-5_wp) then + print *, "✓ 时间验证通过: ", final_time, " ≈ ", config%final_time + else + print *, "✗ 时间验证失败: ", final_time, " ≠ ", config%final_time + end if + + if (final_step > 0) then + print *, "✓ 步数验证通过: ", final_step, " > 0" + else + print *, "✗ 步数验证失败: ", final_step, " ≤ 0" + end if + + print *, "" + + ! 最终判断 + if (state == SOLVER_COMPLETED .and. & + abs(final_time - config%final_time) < 1e-5_wp .and. & + final_step > 0) then + print *, "=========================================" + print *, " 所有测试通过! ✓" + print *, "=========================================" + else + print *, "=========================================" + print *, " 测试失败 ✗" + print *, "=========================================" + end if + +end program test_physics_solver_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_registry.f90 new file mode 100644 index 00000000..e82651ff --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_registry.f90 @@ -0,0 +1,87 @@ +! tests/test_registry.f90 (原test_minimal_simple.f90) +program test_registry + use base_modules, only: wp + use registry_module + use config_module + use mesh_module + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== 注册系统功能测试 ===" + print *, "" + + ! 测试1: 配置系统 + print *, "1. 测试配置系统" + print *, "--------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! 测试2: 网格系统 + print *, "2. 测试网格系统" + print *, "--------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! 测试3: 注册系统 + print *, "3. 测试注册系统" + print *, "--------------" + + call registry_init() + + ! 注册组件 + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call list_components() + print *, "注册表大小: ", registry_get_size() + print *, "" + + ! 测试组件查找 + print *, "4. 测试组件查找" + print *, "--------------" + + if (has_component_simple("reconstructor", "eno")) then + print *, "找到: reconstructor.eno" + else + print *, "未找到: reconstructor.eno" + end if + + if (has_component_simple("reconstructor", "unknown")) then + print *, "找到: reconstructor.unknown" + else + print *, "未找到: reconstructor.unknown" + end if + print *, "" + + ! 测试获取可用组件 + print *, "5. 测试注册系统功能" + print *, "------------------" + print *, "注册表已初始化: ", registry_is_initialized() + print *, "组件数量: ", registry_get_size() + print *, "" + + ! 清理 + call registry_cleanup() + + print *, "=== 注册系统测试完成 ===" + +end program test_registry \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_simple_link.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_simple_link.f90 new file mode 100644 index 00000000..71cc614e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_simple_link.f90 @@ -0,0 +1,78 @@ +! tests/test_simple_link.f90 +program test_simple_link + use base_modules, only: wp ! ← 添加这行 + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call registry_init() + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call list_components() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component_simple("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component_simple("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Cleanup + call registry_cleanup() + + print *, "=== Minimal test completed successfully ===" + +end program test_simple_link \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_solver_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_solver_base.f90 new file mode 100644 index 00000000..6cfe47e4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_solver_base.f90 @@ -0,0 +1,99 @@ +! tests/test_solver_base.f90 (修复版) +program test_solver_base + ! 所有 USE 语句必须在程序开始处 + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + use mesh_module, only: mesh_type + use solver_base_module, only: solver_base, SOLVER_UNINITIALIZED, & + SOLVER_INITIALIZED, SOLVER_COMPLETED, SOLVER_ERROR + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(solver_base) :: solver + integer :: state + + print *, "=== Solver Base Test ===" + print *, "" + + ! 测试1: 创建求解器 + print *, "1. Creating solver..." + print *, "----------------------" + + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%dt = 0.01_wp + + call config_print(config) + + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + solver = solver_base(config, mesh) + call solver%print_info() + print *, "" + + ! 测试2: 初始化 + print *, "2. Initializing solver..." + print *, "-------------------------" + + call solver%initialize() + state = solver%get_state() + print *, "State after initialization: ", state + print *, "Expected: ", SOLVER_INITIALIZED + print *, "Match? ", state == SOLVER_INITIALIZED + print *, "Error message: '", trim(solver%get_error()), "'" + print *, "" + + ! 测试3: 运行求解器 + print *, "3. Running solver..." + print *, "--------------------" + + call solver%run_to_time(0.05_wp) + state = solver%get_state() + print *, "State after run: ", state + print *, "Expected: ", SOLVER_COMPLETED + print *, "Match? ", state == SOLVER_COMPLETED + print *, "Current time: ", solver%current_time + print *, "Current step: ", solver%current_step + print *, "" + + ! 测试4: 再次运行(从已完成状态) + print *, "4. Running again from completed state..." + print *, "----------------------------------------" + + ! 需要先清理才能重新运行 + call solver%cleanup() + call solver%initialize() + call solver%run_to_time(0.1_wp) + + call solver%print_info() + print *, "" + + ! 测试5: 错误处理 + print *, "5. Testing error states..." + print *, "--------------------------" + + ! 创建一个未初始化的求解器 + call solver%cleanup() + state = solver%get_state() + print *, "Uninitialized state: ", state + print *, "Expected: ", SOLVER_UNINITIALIZED + print *, "Match? ", state == SOLVER_UNINITIALIZED + + ! 尝试运行未初始化的求解器 + call solver%run_to_time(0.01_wp) + state = solver%get_state() + print *, "State after error: ", state + print *, "Expected: ", SOLVER_ERROR + print *, "Match? ", state == SOLVER_ERROR + print *, "Error message: '", trim(solver%get_error()), "'" + print *, "" + + print *, "=== Solver Base Test Complete ===" + print *, "✓ Solver base class works" + print *, "✓ State management works" + print *, "✓ Time stepping framework works" + print *, "✓ Error handling works" + +end program test_solver_base \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_solver_framework.f90 b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_solver_framework.f90 new file mode 100644 index 00000000..6754323d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03d/tests/test_solver_framework.f90 @@ -0,0 +1,91 @@ +! tests/test_solver_framework.f90 +program test_solver_framework + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use mesh_module, only: mesh_type + use solver_module, only: cfd_solver, solver_create, solver_run, solver_cleanup + use solver_module, only: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, & + SOLVER_COMPLETED, SOLVER_ERROR + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(cfd_solver) :: solver + + print *, "=== 求解器框架测试 ===" + print *, "" + + ! 测试1: 基本创建 + print *, "1. 测试求解器创建..." + print *, "----------------------" + + ! 创建配置 + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.0_real64 + config%dt = 0.01_real64 + + call config_print(config) + print *, "" + + ! 创建网格 + call mesh%init(xmin=0.0_real64, xmax=2.0_real64, ncells=20) + call mesh%print_info() + print *, "" + + ! 创建求解器 + solver = solver_create(config, mesh) + print *, "✓ 求解器创建成功" + print *, " 状态: ", solver%get_state() + print *, "" + + ! 测试2: 求解器初始化 + print *, "2. 测试求解器初始化..." + print *, "------------------------" + + call solver%initialize() + print *, "✓ 求解器初始化完成" + print *, " 状态: ", solver%get_state() + print *, " 错误信息: '", trim(solver%get_error()), "'" + print *, "" + + ! 测试3: 简单运行 + print *, "3. 测试求解器运行..." + print *, "----------------------" + + call solver_run(solver, 0.05_real64) ! 运行到0.05秒 + print *, "✓ 求解器运行完成" + print *, " 最终状态: ", solver%get_state() + print *, "" + + ! 测试4: 清理 + print *, "4. 测试求解器清理..." + print *, "----------------------" + + call solver_cleanup(solver) + print *, "✓ 求解器清理完成" + print *, " 状态: ", solver%get_state() + print *, "" + + ! 测试5: 错误处理 + print *, "5. 测试错误处理..." + print *, "-------------------" + + ! 尝试重复初始化 + call solver%initialize() + print *, " 重复初始化状态: ", solver%get_state() + print *, " 错误信息: '", trim(solver%get_error()), "'" + + call solver_cleanup(solver) + print *, "" + + print *, "=== 框架测试总结 ===" + print *, "✓ 求解器创建/初始化/运行/清理流程验证完成" + print *, "✓ 状态管理正常工作" + print *, "✓ 错误处理机制就绪" + print *, "" + print *, "下一步: 添加实际数值计算功能" + +end program test_solver_framework \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03e/CMakeLists.txt new file mode 100644 index 00000000..55859dc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) +add_subdirectory(examples) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/README.md b/example/1d-linear-convection/weno3/fortran/registry/03e/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/examples/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03e/examples/CMakeLists.txt new file mode 100644 index 00000000..389925bb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/examples/CMakeLists.txt @@ -0,0 +1,23 @@ +# examples/CMakeLists.txt +message(STATUS "配置示例程序...") + +# 主示例程序:ENO/WENO对比 +add_executable(run_eno_weno + run_eno_weno.f90 +) + +target_link_libraries(run_eno_weno + PRIVATE + solver + infrastructure + core + physics + manager + results # ← 新增链接结果模块 +) + +install(TARGETS run_eno_weno + RUNTIME DESTINATION bin/examples +) + +message(STATUS "示例程序配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/examples/run_eno_weno.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/examples/run_eno_weno.f90 new file mode 100644 index 00000000..ffa6ff9d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/examples/run_eno_weno.f90 @@ -0,0 +1,327 @@ +! examples/run_eno_weno.f90 (修正版) +program run_eno_weno + ! Example program: ENO/WENO comparison analysis + + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction, config_print + use mesh_module, only: mesh_type + use registry_module, only: registry_init, registry_cleanup, initialize_default_components + use physics_solver_module, only: physics_solver, SOLVER_COMPLETED + use results_module, only: results_saver, save_results ! 修改:只导入需要的内容 + + implicit none + + type(cfd_config) :: config_eno3, config_weno3, config_weno5 + type(mesh_type) :: mesh + type(physics_solver) :: solver_eno3, solver_weno3, solver_weno5 + + ! 结果保存器 + type(results_saver) :: saver ! 直接声明类型 + + ! Variables to save results + real(wp) :: time_eno3, time_weno3, time_weno5 + integer :: steps_eno3, steps_weno3, steps_weno5 + integer :: state_eno3, state_weno3, state_weno5 + logical :: all_success + + ! Debug: print start marker + print *, "==========================================" + print *, "START: ENO/WENO Comparison Analysis" + print *, "==========================================" + print *, "" + + ! Step 0: Initialize system + print *, "[STEP 0] Initializing system..." + print *, "--------------------------------" + + call registry_init(verbose=.true.) + print *, "Registry initialized" + + call initialize_default_components() + print *, "Default components registered" + print *, "" + + ! Step 1: Create mesh + print *, "[STEP 1] Creating computational mesh..." + print *, "----------------------------------------" + + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=40) + call mesh%print_info() + print *, "" + + ! 创建结果保存器 - 使用构造函数而不是赋值 + saver = results_saver("results", .true.) ! 直接使用构造函数 + + ! ========== ENO3 Solver ========== + print *, "[STEP 2] Configuring and running ENO3 solver..." + print *, "-----------------------------------------------" + + ! Configure ENO3 + config_eno3%verbose = .true. + config_eno3%ic_type = "step" + config_eno3%wave_speed = 1.0_wp + config_eno3%final_time = 0.625_wp + config_eno3%dt = 0.0025_wp + config_eno3%rk_order = 2 + config_eno3%boundary_type = "periodic" + config_eno3%equation_type = "linear_advection" + config_eno3%problem_type = "linear_advection" + config_eno3%enable_physics = .true. + config_eno3%domain_length = 2.0_wp + + call config_with_reconstruction(config_eno3, "eno", 3) + + print *, "ENO3 configuration:" + call config_print(config_eno3) + print *, "" + + ! Create and run ENO3 solver + print *, "Creating ENO3 solver instance..." + ! 替换这三行: + ! call physics_solver_constructor(solver_eno3, config_eno3, mesh) + + ! 改为: + solver_eno3 = physics_solver(config_eno3, mesh) + + print *, "Initializing ENO3 solver..." + call solver_eno3%initialize() + + print *, "Running ENO3 solver..." + call solver_eno3%run_to_time(config_eno3%final_time) + + ! Immediately save ENO3 results + time_eno3 = solver_eno3%current_time + steps_eno3 = solver_eno3%current_step + state_eno3 = solver_eno3%get_state() + + print *, "ENO3 solver completed" + print *, " Final time: ", time_eno3 + print *, " Total steps: ", steps_eno3 + print *, " State: ", state_eno3 + print *, "" + + ! 保存ENO3结果到文件 + call save_results(saver, "ENO3", & + solver_eno3%config, solver_eno3%mesh, solver_eno3%domain, & + solver_eno3%solution, & + solver_eno3%current_time, solver_eno3%current_step, solver_eno3%get_state()) + + ! ========== WENO3 Solver ========== + print *, "[STEP 3] Configuring and running WENO3 solver..." + print *, "------------------------------------------------" + + ! Configure WENO3 + config_weno3%verbose = .true. + config_weno3%ic_type = "step" + config_weno3%wave_speed = 1.0_wp + config_weno3%final_time = 0.625_wp + config_weno3%dt = 0.0025_wp + config_weno3%rk_order = 2 + config_weno3%boundary_type = "periodic" + config_weno3%equation_type = "linear_advection" + config_weno3%problem_type = "linear_advection" + config_weno3%enable_physics = .true. + config_weno3%domain_length = 2.0_wp + + call config_with_reconstruction(config_weno3, "weno3", 3) + + print *, "WENO3 configuration:" + call config_print(config_weno3) + print *, "" + + ! Create and run WENO3 solver + print *, "Creating WENO3 solver instance..." + call physics_solver_constructor(solver_weno3, config_weno3, mesh) + + print *, "Initializing WENO3 solver..." + call solver_weno3%initialize() + + print *, "Running WENO3 solver..." + call solver_weno3%run_to_time(config_weno3%final_time) + + ! Immediately save WENO3 results + time_weno3 = solver_weno3%current_time + steps_weno3 = solver_weno3%current_step + state_weno3 = solver_weno3%get_state() + + print *, "WENO3 solver completed" + print *, " Final time: ", time_weno3 + print *, " Total steps: ", steps_weno3 + print *, " State: ", state_weno3 + print *, "" + + ! 保存WENO3结果到文件 + call save_results(saver, "WENO3", & + solver_weno3%config, solver_weno3%mesh, solver_weno3%domain, & + solver_weno3%solution, time_weno3, steps_weno3, state_weno3) + + ! ========== WENO5 Solver ========== + print *, "[STEP 4] Configuring and running WENO5 solver..." + print *, "------------------------------------------------" + + ! Configure WENO5 + config_weno5%verbose = .true. + config_weno5%ic_type = "step" + config_weno5%wave_speed = 1.0_wp + config_weno5%final_time = 0.625_wp + config_weno5%dt = 0.0025_wp + config_weno5%rk_order = 2 + config_weno5%boundary_type = "periodic" + config_weno5%equation_type = "linear_advection" + config_weno5%problem_type = "linear_advection" + config_weno5%enable_physics = .true. + config_weno5%domain_length = 2.0_wp + + call config_with_reconstruction(config_weno5, "weno", 5) + + print *, "WENO5 configuration:" + call config_print(config_weno5) + print *, "" + + ! Create and run WENO5 solver + print *, "Creating WENO5 solver instance..." + call physics_solver_constructor(solver_weno5, config_weno5, mesh) + + print *, "Initializing WENO5 solver..." + call solver_weno5%initialize() + + print *, "Running WENO5 solver..." + call solver_weno5%run_to_time(config_weno5%final_time) + + ! Immediately save WENO5 results + time_weno5 = solver_weno5%current_time + steps_weno5 = solver_weno5%current_step + state_weno5 = solver_weno5%get_state() + + print *, "WENO5 solver completed" + print *, " Final time: ", time_weno5 + print *, " Total steps: ", steps_weno5 + print *, " State: ", state_weno5 + print *, "" + + ! 保存WENO5结果到文件 + call save_results(saver, "WENO5", & + solver_weno5%config, solver_weno5%mesh, solver_weno5%domain, & + solver_weno5%solution, time_weno5, steps_weno5, state_weno5) + + ! ========== Results Summary ========== + print *, "==========================================" + print *, " RESULTS SUMMARY" + print *, "==========================================" + print *, "" + + print *, "Solver Performance Comparison:" + print *, "------------------------------" + + print *, "ENO3:" + print *, " Final time: ", time_eno3 + print *, " Total steps: ", steps_eno3 + print *, " State: ", state_eno3 + print *, " Results saved to: results_ENO3_40.dat" + print *, "" + + print *, "WENO3:" + print *, " Final time: ", time_weno3 + print *, " Total steps: ", steps_weno3 + print *, " State: ", state_weno3 + print *, " Results saved to: results_WENO3_40.dat" + print *, "" + + print *, "WENO5:" + print *, " Final time: ", time_weno5 + print *, " Total steps: ", steps_weno5 + print *, " State: ", state_weno5 + print *, " Results saved to: results_WENO5_40.dat" + print *, "" + + ! ========== Final Judgment ========== + print *, "==========================================" + print *, " FINAL JUDGMENT" + print *, "==========================================" + print *, "" + + all_success = (state_eno3 == SOLVER_COMPLETED) .and. & + (state_weno3 == SOLVER_COMPLETED) .and. & + (state_weno5 == SOLVER_COMPLETED) + + if (all_success) then + print *, "✓ ALL SOLVERS SUCCESSFULLY COMPLETED!" + print *, "" + print *, "Parameter Summary:" + print *, " Grid cells: ", mesh%ncells + print *, " Time step: ", config_eno3%dt + print *, " Final time: ", config_eno3%final_time + print *, " Wave speed: ", config_eno3%wave_speed + print *, " IC type: ", trim(config_eno3%ic_type) + print *, "" + print *, "Performance Comparison:" + print *, " ENO3: ", steps_eno3, " steps" + print *, " WENO3: ", steps_weno3, " steps" + print *, " WENO5: ", steps_weno5, " steps" + print *, "" + print *, "To visualize results:" + print *, " python ../python/plot_results.py --auto" + else + print *, "✗ SOME SOLVERS FAILED" + print *, "" + print *, "Failure Analysis:" + if (state_eno3 /= SOLVER_COMPLETED) then + print *, " • ENO3 failed with state: ", state_eno3 + end if + if (state_weno3 /= SOLVER_COMPLETED) then + print *, " • WENO3 failed with state: ", state_weno3 + end if + if (state_weno5 /= SOLVER_COMPLETED) then + print *, " • WENO5 failed with state: ", state_weno5 + end if + end if + + print *, "" + print *, "==========================================" + print *, " ANALYSIS COMPLETE" + print *, "==========================================" + + ! ========== Cleanup ========== + print *, "" + print *, "[STEP 5] Cleaning up system..." + print *, "--------------------------------" + + call solver_eno3%cleanup() + call solver_weno3%cleanup() + call solver_weno5%cleanup() + call registry_cleanup() + + print *, "All solvers cleaned up" + print *, "Registry cleaned up" + print *, "" + + ! ========== Wait for user input before exit ========== + print *, "==========================================" + print *, "Press ENTER to exit..." + print *, "==========================================" + + ! Wait for user input (uncomment if needed) + ! read(*,*) + + ! Alternative: add a small delay + print *, "Program will exit in 3 seconds..." + call sleep(3) + + print *, "" + print *, "==========================================" + print *, " PROGRAM END" + print *, "==========================================" + +contains + + ! 辅助函数:显式创建physics_solver(如果原始代码使用函数而不是子程序) + subroutine physics_solver_constructor(solver, config, mesh) + type(physics_solver), intent(out) :: solver + type(cfd_config), intent(in) :: config + type(mesh_type), intent(in) :: mesh + + ! 使用赋值构造函数(如果physics_solver_module中有相应的接口) + solver = physics_solver(config, mesh) + end subroutine physics_solver_constructor + +end program run_eno_weno \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/python/plot_results.py b/example/1d-linear-convection/weno3/fortran/registry/03e/python/plot_results.py new file mode 100644 index 00000000..cf8e4017 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/python/plot_results.py @@ -0,0 +1,419 @@ +#!/usr/bin/env python3 +# python/plot_results.py +""" +Fortran CFD 结果可视化脚本 +与Julia的plotter.jl功能类似,但直接读取Fortran生成的文本文件 +""" + +import numpy as np +import matplotlib.pyplot as plt +import os +import sys +import glob +from pathlib import Path +import re + +class FortranResultsPlotter: + """Fortran结果绘图器""" + + def __init__(self, style='default'): + self.style = style + self.setup_styles() + + def setup_styles(self): + """设置绘图样式""" + if self.style == 'default': + self.styles = { + 'numerical': { + 'color': 'blue', + 'linestyle': '-', + 'marker': 'o', + 'markerfacecolor': 'none', + 'markersize': 4, + 'linewidth': 1 + }, + 'analytical': { + 'color': 'red', + 'linestyle': '--', + 'marker': '', + 'linewidth': 1.5 + }, + 'comparison': [ + {'color': 'black', 'linestyle': '-', 'marker': 'o', 'markerfacecolor': 'none'}, + {'color': 'blue', 'linestyle': '--', 'marker': 's', 'markerfacecolor': 'none'}, + {'color': 'green', 'linestyle': ':', 'marker': '^', 'markerfacecolor': 'none'} + ] + } + + def read_fortran_results(self, filename): + """读取Fortran生成的结果文件""" + data = {} + + try: + with open(filename, 'r', encoding='utf-8', errors='ignore') as f: + lines = f.readlines() + + data_lines = [] + in_data_section = False # 新状态:是否在数据区域 + + for line in lines: + line = line.strip() + if not line: + continue + + # 跳过所有分隔线 + if line.startswith("===="): + continue + + # 检测数据区域开始 + if line == "DATA: x, numerical, analytical": + in_data_section = True + continue + + if not in_data_section: + # 解析头部信息 + if "Solver:" in line: + data['solver'] = line.split(":", 1)[1].strip() + elif "Scheme:" in line: + data['scheme'] = line.split(":", 1)[1].strip() + elif "Order:" in line: + if "RK Order:" in line: + data['rk_order'] = int(line.split(":", 1)[1].strip()) + else: + data['order'] = int(line.split(":", 1)[1].strip()) + elif "Current Time:" in line: + data['time'] = float(line.split(":", 1)[1].strip()) + elif "Grid Points:" in line: + data['n_points'] = int(line.split(":", 1)[1].strip()) + else: + # 解析数据行 + parts = line.split() + if len(parts) >= 3: + try: + x = float(parts[0]) + numerical = float(parts[1]) + analytical = float(parts[2]) + data_lines.append([x, numerical, analytical]) + except ValueError: + continue # 忽略无法解析的行 + + if data_lines: + import numpy as np + data_array = np.array(data_lines) + data['x'] = data_array[:, 0] + data['numerical'] = data_array[:, 1] + data['analytical'] = data_array[:, 2] + + print(f"Read {len(data['x'])} points from {filename}") + print(f" Solver: {data.get('solver', 'N/A')}") + print(f" Scheme: {data.get('scheme', 'N/A')} order {data.get('order', 'N/A')}") + print(f" Time: {data.get('time', 'N/A')}") + + return data + else: + print(f"Warning: No data found in {filename}") + return None + + except Exception as e: + print(f"Error reading {filename}: {e}") + return None + + def plot_single_result(self, filename, title=None, show=True, save_path=None): + """绘制单个结果文件""" + data = self.read_fortran_results(filename) + if data is None: + return False + + fig, ax = plt.subplots(figsize=(10, 6)) + + # 自动生成标题 + if title is None: + title = f"1D Convection (t={data['time']:.3f})\n" + title += f"{data['order']}th-order {data['scheme'].upper()} + {data['rk_order']}nd-order RK" + + # 绘制数值解 + ax.plot(data['x'], data['numerical'], + label=f"Numerical ({data['scheme'].upper()}{data['order']})", + **self.styles['numerical']) + + # 绘制解析解 + ax.plot(data['x'], data['analytical'], + label="Analytical", + **self.styles['analytical']) + + # 设置图形属性 + ax.set_title(title, fontsize=12) + ax.set_xlabel("x", fontsize=10) + ax.set_ylabel("u", fontsize=10) + ax.legend(fontsize=9) + ax.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + + # 保存或显示 + if save_path: + plt.savefig(save_path, dpi=150, bbox_inches='tight') + print(f"Saved plot to {save_path}") + + if show: + plt.show() + else: + plt.close() + + return True + + def format_scheme_label(self, scheme: str, order: int) -> str: + s = scheme.upper() + if s.startswith("WENO"): + return f"WENO{order}" + else: + return f"{s}{order}" + + def plot_comparison(self, filenames, labels=None, title=None, show=True, save_path=None): + """比较多个结果文件""" + all_data = [] + print(f"plot_comparison filenames={filenames}") + + # 读取所有文件 + for filename in filenames: + print(f"plot_comparison filename={filename}") + data = self.read_fortran_results(filename) + if data is not None: + all_data.append(data) + + if not all_data: + print("No valid data to plot") + return False + + fig, axes = plt.subplots(2, 2, figsize=(14, 10)) + + # 子图1:所有方法比较 + for i, data in enumerate(all_data): + #print(f"i,all_data={i,all_data}") + if labels and i < len(labels): + label = labels[i] + else: + label = f"{data['scheme'].upper()}{data['order']}" + + style_idx = i % len(self.styles['comparison']) + style = self.styles['comparison'][style_idx] + + axes[0, 0].plot(data['x'], data['numerical'], + label=label, **style) + + axes[0, 0].plot(all_data[0]['x'], all_data[0]['analytical'], + label="Analytical", **self.styles['analytical']) + + axes[0, 0].set_xlabel('x', fontsize=12) + axes[0, 0].set_ylabel('u(x)', fontsize=12) + axes[0, 0].set_title('Numerical Solutions Comparison', fontsize=14) + axes[0, 0].legend() + axes[0, 0].grid(True, alpha=0.3) + + # 子图2:误差分析 + for i, data in enumerate(all_data): + if labels and i < len(labels): + label = labels[i] + else: + label = f"{data['scheme'].upper()}{data['order']}" + + error = np.abs(data['numerical'] - data['analytical']) + axes[0, 1].semilogy(data['x'], error, label=label) + + axes[0, 1].set_xlabel('x', fontsize=12) + axes[0, 1].set_ylabel('Absolute Error', fontsize=12) + axes[0, 1].set_title('Error Comparison', fontsize=14) + axes[0, 1].legend() + axes[0, 1].grid(True, alpha=0.3) + + # 子图3:数值解细节(放大) + x_min, x_max = all_data[0]['x'].min(), all_data[0]['x'].max() + zoom_center = 1.0 # 阶跃函数位置附近 + zoom_width = 0.3 + + for i, data in enumerate(all_data): + if labels and i < len(labels): + label = labels[i] + else: + label = f"{data['scheme'].upper()}{data['order']}" + + style_idx = i % len(self.styles['comparison']) + style = self.styles['comparison'][style_idx] + + axes[1, 0].plot(data['x'], data['numerical'], label=label, **style) + + axes[1, 0].plot(all_data[0]['x'], all_data[0]['analytical'], + label="Analytical", **self.styles['analytical']) + + axes[1, 0].set_xlim(zoom_center - zoom_width/2, zoom_center + zoom_width/2) + axes[1, 0].set_xlabel('x', fontsize=12) + axes[1, 0].set_ylabel('u(x)', fontsize=12) + axes[1, 0].set_title('Zoomed View (x ≈ 1.0)', fontsize=14) + axes[1, 0].legend() + axes[1, 0].grid(True, alpha=0.3) + + # 子图4:性能统计 + axes[1, 1].axis('off') + + # 计算并显示L2误差 + errors = [] + schemes = [] + + for data in all_data: + error = np.sqrt(np.mean((data['numerical'] - data['analytical'])**2)) + errors.append(error) + schemes.append(f"{data['scheme'].upper()}{data['order']}") + + # 创建表格数据 + table_data = [] + for i, (scheme, error) in enumerate(zip(schemes, errors)): + table_data.append([scheme, f"{error:.2e}"]) + + # 在子图中显示表格 + table = axes[1, 1].table(cellText=table_data, + colLabels=['Scheme', 'L2 Error'], + loc='center', + cellLoc='center', + colWidths=[0.3, 0.4]) + + table.auto_set_font_size(False) + table.set_fontsize(10) + table.scale(1, 1.5) + + axes[1, 1].set_title('Performance Summary (L2 Error)', fontsize=14) + + # 设置总标题 + if title is None: + time = all_data[0]['time'] + schemes_str = ", ".join([self.format_scheme_label(d['scheme'], d['order']) for d in all_data]) + title = f"1D Convection Comparison (t={time:.3f})\n{schemes_str}" + + fig.suptitle(title, fontsize=16) + plt.tight_layout() + + # 保存或显示 + if save_path: + plt.savefig(save_path, dpi=150, bbox_inches='tight') + print(f"Saved comparison plot to {save_path}") + + if show: + plt.show() + else: + plt.close() + + return True + + def get_scheme_from_filename(self, filename): + print(f"filename={filename}") + stem = Path(filename).stem # e.g., "results_ENO3_40" + parts = stem.split('_') + if len(parts) >= 2: + return parts[1] # "ENO3", "WENO3", "WENO5" + return "" + + def plot_eno_weno_comparison(self, result_dir=".", save_path="eno_weno_comparison.png"): + """自动绘制ENO/WENO对比图(类似Julia的功能)""" + # 查找结果文件 + pattern = os.path.join(result_dir, "results_*.dat") + files = glob.glob(pattern) + + if not files: + print(f"No result files found matching {pattern}") + return False + + # 重新分类 + file_info = [] + eno_files = [] + weno3_files = [] + weno5_files = [] + for f in files: + print(f"f={f}") + print(f"type(f)={type(f)}") + scheme = self.get_scheme_from_filename(f) + print(f"scheme={scheme}") + if scheme == "ENO3": + eno_files.append(f) + elif scheme == "WENO3": + weno3_files.append(f) + elif scheme == "WENO5": + weno5_files.append(f) + + # 按求解器类型分类 + print(f"eno_files={eno_files}") + print(f"weno3_files={weno3_files}") + print(f"weno5_files={weno5_files}") + + # 选择最新的文件(如果有多组) + files_to_plot = [] + labels = [] + + if eno_files: + files_to_plot.append(sorted(eno_files)[-1]) + labels.append("ENO3") + + if weno3_files: + files_to_plot.append(sorted(weno3_files)[-1]) + labels.append("WENO3") + + if weno5_files: + files_to_plot.append(sorted(weno5_files)[-1]) + labels.append("WENO5") + + if len(files_to_plot) >= 2: + print(f"Plotting comparison of {len(files_to_plot)} solvers") + return self.plot_comparison(files_to_plot, labels=labels, + save_path=save_path, show=True) + else: + print("Not enough different solvers for comparison") + return False + +def main(): + """主函数""" + import argparse + + parser = argparse.ArgumentParser(description='Fortran CFD Results Visualizer') + parser.add_argument('--file', help='Plot single result file') + parser.add_argument('--compare', nargs='+', help='Compare multiple files') + parser.add_argument('--dir', default='.', help='Directory containing result files') + parser.add_argument('--auto', action='store_true', help='Auto plot ENO/WENO comparison') + parser.add_argument('--save', help='Save plot to file (without showing)') + parser.add_argument('--no-show', action='store_true', help='Don\'t show plot') + + args = parser.parse_args() + + plotter = FortranResultsPlotter() + + if args.file: + # 绘制单个文件 + plotter.plot_single_result(args.file, save_path=args.save, + show=not args.no_show) + + elif args.compare: + # 比较多个文件 + plotter.plot_comparison(args.compare, save_path=args.save, + show=not args.no_show) + + elif args.auto: + # 自动绘制比较图 + save_path = args.save or "eno_weno_comparison.png" + plotter.plot_eno_weno_comparison(args.dir, save_path=save_path) + + else: + # 默认:显示帮助 + parser.print_help() + + # 也显示可用的结果文件 + print("\nAvailable result files:") + pattern = os.path.join(args.dir, "results_*.dat") + files = glob.glob(pattern) + + if files: + for f in sorted(files): + print(f" {os.path.basename(f)}") + + print(f"\nTo plot ENO/WENO comparison automatically:") + print(f" python plot_results.py --auto") + else: + print(" No result files found") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/build.bat new file mode 100644 index 00000000..6fd6dc03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/build.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Project Builder +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python build script with full Intel environment support... +echo. + +python build.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Build failed + pause + exit /b 1 +) + +echo. +echo [INFO] Build completed successfully! +echo. +echo [INFO] To run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/build.py new file mode 100644 index 00000000..3bf6d537 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/build.py @@ -0,0 +1,629 @@ +#!/usr/bin/env python3 +""" +Fortran CFD Project Builder - 完整Python解决方案 +在Python内部处理Intel oneAPI环境配置 +""" + +import os +import sys +import subprocess +import shutil +import argparse +import time +import platform +import tempfile +from pathlib import Path + +class IntelEnvironment: + """Intel oneAPI环境管理器""" + + def __init__(self): + self.setvars_path = None + self.env_vars = {} + + def find_setvars(self): + """查找setvars.bat文件""" + possible_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + r"C:\Program Files (x86)\Intel\oneAPI\compiler\latest\env\vars.bat", + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\setvars.bat"), + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\compiler\latest\env\vars.bat"), + ] + + for path in possible_paths: + if os.path.exists(path): + self.setvars_path = path + return True + + return False + + def setup_environment(self): + """设置Intel环境""" + if not self.find_setvars(): + return False + + try: + # 创建临时的批处理文件来捕获环境变量 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + f.write(f'@echo off\n') + f.write(f'call "{self.setvars_path}" >nul 2>&1\n') + f.write(f'set\n') # 输出所有环境变量 + temp_bat = f.name + + # 运行批处理文件并捕获输出 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + shell=True + ) + + # 解析环境变量 + for line in result.stdout.split('\n'): + line = line.strip() + if '=' in line: + key, value = line.split('=', 1) + self.env_vars[key.strip()] = value.strip() + + # 清理临时文件 + os.unlink(temp_bat) + + # 更新当前进程的环境变量 + os.environ.update(self.env_vars) + + return True + + except Exception as e: + print(f"设置Intel环境失败: {e}") + return False + + def get_compiler_info(self): + """获取编译器信息""" + info = {} + + # 检查ifx编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + env={**os.environ, **self.env_vars} if self.env_vars else os.environ + ) + + if result.returncode == 0: + for line in result.stdout.split('\n'): + if 'Version' in line or '版本' in line: + info['ifx_version'] = line.strip() + break + except: + pass + + # 检查环境变量 + info['ifx_root'] = self.env_vars.get('IFX_ROOT', '') + info['compiler_root'] = self.env_vars.get('ONEAPI_ROOT', '') + + return info + +class BuildSystem: + """构建系统主类""" + + def __init__(self): + self.project_root = Path(__file__).parent.parent + self.build_dir = self.project_root / "build" + self.intel_env = IntelEnvironment() + + # 设置控制台编码 + if sys.platform == "win32": + try: + import ctypes + # 设置控制台输出为UTF-8 + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + except: + pass + + def print_header(self, text): + """打印标题""" + print(f"\n{'='*70}") + print(f" {text}") + print(f"{'='*70}\n") + + def print_step(self, step, total, message): + """打印步骤""" + print(f"[{step}/{total}] {message}...") + + def print_success(self, message): + """打印成功""" + print(f"\033[92m✓ {message}\033[0m") + + def print_error(self, message): + """打印错误""" + print(f"\033[91m✗ {message}\033[0m") + + def print_warning(self, message): + """打印警告""" + print(f"\033[93m! {message}\033[0m") + + def print_info(self, message): + """打印信息""" + print(f"\033[94mℹ {message}\033[0m") + + def check_prerequisites(self): + """检查前提条件""" + self.print_step(1, 6, "检查前提条件") + + # 检查Python版本 + python_version = sys.version.split()[0] + self.print_info(f"Python版本: {python_version}") + + # 检查平台 + self.print_info(f"平台: {platform.system()} {platform.release()}") + self.print_info(f"处理器核心数: {os.cpu_count()}") + + # 检查CMake + try: + result = subprocess.run( + ["cmake", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + version_line = result.stdout.split('\n')[0] + self.print_success(f"CMake: {version_line}") + else: + self.print_error("CMake未找到") + return False + except FileNotFoundError: + self.print_error("CMake未安装") + return False + + return True + + def setup_intel_environment(self, args): + """设置Intel环境""" + self.print_step(2, 6, "配置Intel oneAPI环境") + + if not self.intel_env.find_setvars(): + self.print_warning("未找到Intel oneAPI setvars.bat") + self.print_info("将尝试使用系统环境中的编译器") + + # 检查是否能直接访问编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + self.print_success("Intel编译器在系统PATH中找到") + return True + else: + self.print_warning("Intel编译器未在PATH中找到") + except: + self.print_warning("无法访问Intel编译器") + + return True # 继续,让CMake自己找编译器 + + # 设置环境 + if self.intel_env.setup_environment(): + compiler_info = self.intel_env.get_compiler_info() + + if compiler_info.get('ifx_version'): + self.print_success(f"Intel Fortran编译器: {compiler_info['ifx_version']}") + elif compiler_info.get('ifx_root'): + self.print_success(f"Intel编译器路径: {compiler_info['ifx_root']}") + else: + self.print_success("Intel oneAPI环境配置完成") + + return True + else: + self.print_warning("Intel环境配置失败,将继续使用系统环境") + return True + + def clean_build_directory(self, args): + """清理构建目录""" + if args.clean and self.build_dir.exists(): + self.print_info("清理构建目录...") + try: + shutil.rmtree(self.build_dir) + self.print_success("构建目录已清理") + except Exception as e: + self.print_error(f"清理失败: {e}") + if not args.force: + return False + return True + + def run_command(self, cmd, cwd=None, check=True, env=None): + """运行命令""" + if isinstance(cmd, list): + cmd_str = ' '.join(str(c) for c in cmd if c) + else: + cmd_str = str(cmd) + + print(f" \033[96m$\033[0m {cmd_str}") + + try: + # 合并环境变量 + exec_env = os.environ.copy() + if env: + exec_env.update(env) + if self.intel_env.env_vars: + exec_env.update(self.intel_env.env_vars) + + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=False, + env=exec_env + ) + + # 处理输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or '完成' in line or '生成' in line: + print(f" \033[92m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + if check and result.returncode != 0: + self.print_error(f"命令执行失败,退出码: {result.returncode}") + return False + + return True + + except Exception as e: + self.print_error(f"命令执行异常: {e}") + return False + + def configure_cmake(self, args): + """配置CMake""" + self.print_step(3, 6, "配置CMake项目") + + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ] + + if args.compiler == "ifx": + cmake_cmd.extend(["-T", "fortran=ifx"]) + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + success = self.run_command(cmake_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("CMake配置完成") + else: + self.print_error("CMake配置失败") + + return success + + def build_project(self, args): + """构建项目""" + self.print_step(4, 6, "构建项目") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + success = self.run_command(build_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("项目构建完成") + else: + self.print_error("构建失败") + + return success + + def run_tests_with_environment(self, test_exe): + """运行单个测试,确保有Intel环境""" + try: + # 创建临时的批处理文件来运行测试 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'"{test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'"{test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + return result + + except Exception as e: + print(f"运行测试失败: {e}") + return None + + def run_tests(self, args): + """运行测试""" + self.print_step(5, 6, "运行测试") + + # 查找测试可执行文件 + test_dir = self.build_dir / "bin" / args.build_type + if not test_dir.exists(): + test_dir = self.build_dir / "bin" + if not test_dir.exists(): + test_dir = self.build_dir + + test_files = list(test_dir.glob("test_*.exe")) + + if not test_files: + self.print_warning("未找到测试程序") + return True + + all_passed = True + + for test_exe in sorted(test_files): + test_name = test_exe.stem + self.print_info(f"运行测试: {test_name}") + print(f" {'-'*50}") + + # 运行测试 + result = self.run_tests_with_environment(str(test_exe)) + + if result is None: + self.print_error(f" {test_name} 运行失败") + all_passed = False + continue + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + print(f" {line}") + + if result.returncode == 0: + self.print_success(f" {test_name} 通过") + else: + self.print_error(f" {test_name} 失败 (退出码: {result.returncode})") + all_passed = False + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + print() # 空行 + + return all_passed + + def create_test_runner(self, args): + """创建独立的测试运行器""" + self.print_step(6, 6, "创建测试运行器") + + runner_path = self.build_dir / "run_tests.bat" + + content = f'''@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Test Runner +echo ======================================== +echo. + +REM Setup Intel oneAPI environment +set "SETVARS_PATH={self.intel_env.setvars_path or ''}" +if exist "%SETVARS_PATH%" ( + call "%SETVARS_PATH%" >nul + echo [INFO] Intel environment configured +) else ( + echo [WARNING] Intel environment not found + echo [WARNING] Tests may fail without runtime libraries +) + +echo. + +REM Run all test executables +set "TEST_COUNT=0" +set "PASS_COUNT=0" + +for %%f in ("bin\\{args.build_type}\\test_*.exe") do ( + set /a TEST_COUNT+=1 + echo [TEST %%f] + echo {'-'*50} + + %%f + if errorlevel 1 ( + echo [FAILED] %%f + ) else ( + echo [PASSED] %%f + set /a PASS_COUNT+=1 + ) + echo. +) + +echo ======================================== +echo Tests: %PASS_COUNT%/%TEST_COUNT% passed +if %PASS_COUNT% equ %TEST_COUNT% ( + echo [SUCCESS] All tests passed! +) else ( + echo [FAILURE] Some tests failed +) +echo ======================================== + +pause +''' + + with open(runner_path, 'w', encoding='utf-8') as f: + f.write(content) + + self.print_success(f"测试运行器已创建: {runner_path}") + self.print_info(f"使用方法: cd build && run_tests.bat") + + return runner_path + + def generate_report(self, args, build_time, tests_passed): + """生成构建报告""" + self.print_header("构建完成") + + print(f"项目: {self.project_root.name}") + print(f"构建类型: {args.build_type}") + print(f"编译器: {args.compiler}") + print(f"并行作业: {args.jobs}") + print(f"总耗时: {build_time:.1f}秒") + print(f"测试结果: {'全部通过' if tests_passed else '有失败'}") + + # 显示生成的可执行文件 + bin_dir = self.build_dir / "bin" / args.build_type + if bin_dir.exists(): + print(f"\n生成的可执行文件:") + for exe in sorted(bin_dir.glob("*.exe")): + size_mb = exe.stat().st_size / (1024 * 1024) + print(f" • {exe.name} ({size_mb:.2f} MB)") + + # 显示测试运行器信息 + runner_path = self.build_dir / "run_tests.bat" + if runner_path.exists(): + print(f"\n独立测试运行器:") + print(f" • {runner_path.name}") + print(f" 在Intel oneAPI环境中运行所有测试") + + def run(self): + """运行构建系统""" + parser = argparse.ArgumentParser( + description="Fortran CFD项目构建工具", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认构建 + %(prog)s --clean # 清理后构建 + %(prog)s --build-type Release # Release构建 + %(prog)s --no-tests # 只构建,不运行测试 + %(prog)s -j8 --verbose # 8线程并行构建,详细输出 + """ + ) + + parser.add_argument("--build-type", choices=["Debug", "Release"], + default="Debug", help="构建类型") + parser.add_argument("--compiler", choices=["ifx", "ifort"], + default="ifx", help="Fortran编译器") + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-tests", action="store_true", + help="跳过测试") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + + args = parser.parse_args() + + # 开始构建 + start_time = time.time() + + self.print_header("Fortran CFD 项目构建系统") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 检查前提条件 + if not self.check_prerequisites(): + if not args.force: + return 1 + + # 2. 设置Intel环境 + if not self.setup_intel_environment(args): + if not args.force: + return 1 + + # 3. 清理目录 + if not self.clean_build_directory(args): + if not args.force: + return 1 + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 4. 配置CMake + if not self.configure_cmake(args): + if not args.force: + return 1 + + # 5. 构建项目 + if not self.build_project(args): + if not args.force: + return 1 + + # 6. 运行测试和创建测试运行器 + tests_passed = True + if not args.no_tests: + tests_passed = self.run_tests(args) + + # 创建测试运行器 + self.create_test_runner(args) # 传递 args 参数 + + # 7. 生成报告 + build_time = time.time() - start_time + self.generate_report(args, build_time, tests_passed) + + return 0 if tests_passed else 1 + + except KeyboardInterrupt: + self.print_error("\n构建被用户中断") + return 1 + except Exception as e: + self.print_error(f"构建过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + builder = BuildSystem() + return builder.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/run_all_steps.bat b/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/run_all_steps.bat new file mode 100644 index 00000000..d506149b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/run_all_steps.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo CFD Project: All Steps +echo ======================================== +echo. + +echo [INFO] Starting Step 1: Physics Modules Test... +call run_step1.bat + +if errorlevel 1 ( + echo [ERROR] Step 1 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Starting Step 2: Configuration Physics Update... +call run_step2.bat + +if errorlevel 1 ( + echo [ERROR] Step 2 failed + pause + exit /b 1 +) + +echo. +echo ======================================== +echo All Steps Completed Successfully! +echo ======================================== +echo. +echo [INFO] Next: Update component manager for physics support +echo [INFO] Run: run_step3.bat (to be created) +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/run_example.py b/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/run_example.py new file mode 100644 index 00000000..d7c19917 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/run_example.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +# scripts/run_example.py +""" +运行ENO/WENO示例程序 +""" + +import os +import sys +import subprocess +from pathlib import Path + +# 添加当前目录到路径 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import BuildSystem +except ImportError: + print("错误: 找不到build.py,请确保在scripts目录中运行") + sys.exit(1) + +def run_example(): + """运行示例程序""" + builder = BuildSystem() + + print("\n" + "="*70) + print(" 运行ENO/WENO对比示例程序") + print("="*70 + "\n") + + # 检查是否已构建 + exe_path = builder.build_dir / "bin" / "Debug" / "example_eno_weno_comparison.exe" + + if not exe_path.exists(): + print("示例程序未构建,先构建项目...") + print("-"*50) + + # 使用简化的构建 + result = subprocess.run( + ["python", "build.py", "--no-tests", "--clean"], + cwd=builder.project_root / "scripts", + capture_output=True, + text=True + ) + + if result.returncode != 0: + print("构建失败:") + print(result.stderr) + return False + + # 运行示例程序 + print("运行示例程序...") + print("-"*50) + + try: + result = subprocess.run( + [str(exe_path)], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace' + ) + + print(result.stdout) + + if result.stderr: + print("标准错误输出:") + print(result.stderr) + + return result.returncode == 0 + + except Exception as e: + print(f"运行示例程序失败: {e}") + return False + +def main(): + """主函数""" + success = run_example() + + if success: + print("\n" + "="*70) + print(" ✓ 示例程序运行成功") + print("="*70) + return 0 + else: + print("\n" + "="*70) + print(" ✗ 示例程序运行失败") + print("="*70) + return 1 + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/run_step1.bat b/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/run_step1.bat new file mode 100644 index 00000000..0b6b1f17 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/run_step1.bat @@ -0,0 +1,39 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Step 1: Physics Modules Test +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python step1 script with full Intel environment support... +echo. + +python run_step1.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Step 1 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Step 1 completed successfully! +echo. +echo [INFO] Next step: Update config to include physics settings +echo [INFO] Run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/run_step1.py b/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/run_step1.py new file mode 100644 index 00000000..5e087a69 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/run_step1.py @@ -0,0 +1,319 @@ +#!/usr/bin/env python3 +""" +Step 1: Physics Modules Test +扩展build.py,专门用于测试物理模块 +""" + +import os +import sys +import subprocess +import time +from pathlib import Path + +# 添加当前目录到路径,以便导入build.py的类 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import IntelEnvironment, BuildSystem +except ImportError: + print("Error: Cannot import build.py. Make sure build.py is in the same directory.") + sys.exit(1) + +class Step1System(BuildSystem): + """Step 1 测试系统,继承自BuildSystem""" + + def __init__(self): + super().__init__() + self.test_name = "test_physics_minimal" + self.test_exe = None + + def find_test_executable(self): + """查找测试可执行文件""" + possible_paths = [ + self.build_dir / "bin" / "Debug" / f"{self.test_name}.exe", + self.build_dir / "Debug" / f"{self.test_name}.exe", + self.build_dir / f"{self.test_name}.exe", + self.build_dir / "bin" / f"{self.test_name}.exe", + ] + + for path in possible_paths: + if path.exists(): + self.test_exe = path + self.print_success(f"Found test executable: {path}") + return True + + # 如果没有找到,尝试搜索 + self.print_warning(f"Could not find {self.test_name}.exe") + self.print_info("Searching for test executables...") + + try: + result = subprocess.run( + ["dir", str(self.build_dir), "/s", "/b", "*.exe"], + capture_output=True, + text=True, + encoding='utf-8', + shell=True + ) + + if result.returncode == 0: + test_files = [line.strip() for line in result.stdout.split('\n') + if line and 'test_' in line.lower()] + + if test_files: + self.print_info("Found test files:") + for test_file in test_files: + self.print_info(f" {test_file}") + return False + except: + pass + + return False + + def run_test_with_intel_env(self): + """在Intel环境下运行测试""" + if not self.test_exe: + self.print_error("No test executable found") + return False + + self.print_step(1, 2, f"Running test: {self.test_exe.name}") + + try: + # 创建临时的批处理文件来运行测试(包含Intel环境) + import tempfile + + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + # 设置Intel环境 + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'echo [INFO] Intel environment configured\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'echo [WARNING] Intel environment not found\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + self.print_info(f"Command: {temp_bat}") + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower() or 'fail' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or 'pass' in line.lower() or '✓' in line: + print(f" \033[92m{line}\033[0m") + elif '=' in line or '---' in line: + print(f" \033[96m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + self.print_warning("Test stderr output:") + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + self.print_step(2, 2, "Test execution completed") + + if result.returncode == 0: + self.print_success("Test passed") + return True + else: + self.print_error(f"Test failed (exit code: {result.returncode})") + return False + + except Exception as e: + self.print_error(f"Failed to run test: {e}") + return False + + def build_project_if_needed(self, args): + """如果需要,构建项目""" + if args.no_build: + self.print_info("Skipping build (--no-build flag)") + return True + + self.print_step(1, 3, "Building project") + + # 调用父类的构建方法 + build_args = argparse.Namespace() + build_args.clean = args.clean + build_args.build_type = "Debug" + build_args.compiler = "ifx" + build_args.no_tests = True # 不运行所有测试 + build_args.jobs = os.cpu_count() + build_args.verbose = args.verbose + build_args.force = args.force + + # 清理构建目录 + if args.clean and self.build_dir.exists(): + self.print_info("Cleaning build directory...") + import shutil + try: + shutil.rmtree(self.build_dir) + self.print_success("Build directory cleaned") + except Exception as e: + self.print_error(f"Clean failed: {e}") + if not args.force: + return False + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 配置CMake + self.print_step(2, 3, "Configuring CMake") + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-DCMAKE_BUILD_TYPE=Debug", + "-T", "fortran=ifx", + ] + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + if not self.run_command(cmake_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + # 构建项目 + self.print_step(3, 3, "Building project") + build_cmd = [ + "cmake", + "--build", ".", + "--config", "Debug", + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + if not self.run_command(build_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + self.print_success("Build completed") + return True + + def run(self): + """运行Step 1测试""" + parser = argparse.ArgumentParser( + description="Step 1: Physics Modules Test", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认运行 + %(prog)s --clean # 清理后构建并测试 + %(prog)s --no-build # 只运行测试,不重新构建 + %(prog)s --verbose # 详细输出 + %(prog)s -j4 # 使用4个并行作业构建 + """ + ) + + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-build", action="store_true", + help="不重新构建,直接运行测试") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + + args = parser.parse_args() + + # 开始测试 + start_time = time.time() + + self.print_header("Step 1: Physics Modules Implementation Test") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 设置Intel环境 + self.print_step(1, 4, "Setting up Intel oneAPI environment") + if not self.setup_intel_environment(args): + if not args.force: + self.print_error("Intel environment setup failed") + return 1 + self.print_warning("Intel environment setup failed, continuing...") + + # 2. 构建项目(如果需要) + self.print_step(2, 4, "Building project if needed") + if not self.build_project_if_needed(args): + if not args.force: + return 1 + + # 3. 查找测试可执行文件 + self.print_step(3, 4, "Finding test executable") + if not self.find_test_executable(): + if not args.force: + return 1 + self.print_warning("Test executable not found, but continuing due to --force") + return 0 + + # 4. 运行测试 + self.print_step(4, 4, "Running physics module test") + test_passed = self.run_test_with_intel_env() + + # 生成报告 + test_time = time.time() - start_time + self.print_header("Step 1 Complete") + + print(f"测试: {'通过 ✓' if test_passed else '失败 ✗'}") + print(f"测试程序: {self.test_exe.name if self.test_exe else '未找到'}") + print(f"总耗时: {test_time:.1f}秒") + + if test_passed: + print(f"\n下一步: 更新配置以包含物理设置") + print(f"建议: 修改config.f90,添加physics相关字段") + return 0 + else: + if args.force: + self.print_warning("测试失败,但由于--force标志继续执行") + return 0 + return 1 + + except KeyboardInterrupt: + self.print_error("\n测试被用户中断") + return 1 + except Exception as e: + self.print_error(f"测试过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + system = Step1System() + return system.run() + +if __name__ == "__main__": + # 需要导入argparse + import argparse + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/run_step2.bat b/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/run_step2.bat new file mode 100644 index 00000000..9c1f62de --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/run_step2.bat @@ -0,0 +1,39 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Step 2: Configuration Physics Update +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python step2 script with full Intel environment support... +echo. + +python run_step2.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Step 2 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Step 2 completed successfully! +echo. +echo [INFO] Next step: Update component manager to support physics +echo [INFO] Run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/run_step2.py b/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/run_step2.py new file mode 100644 index 00000000..c16b7608 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/scripts/run_step2.py @@ -0,0 +1,284 @@ +#!/usr/bin/env python3 +""" +Step 2: Configuration Physics Update +测试配置模块的物理功能更新 +""" + +import os +import sys +import subprocess +import time +from pathlib import Path + +# 添加当前目录到路径,以便导入build.py的类 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import IntelEnvironment, BuildSystem +except ImportError: + print("Error: Cannot import build.py. Make sure build.py is in the same directory.") + sys.exit(1) + +class Step2System(BuildSystem): + """Step 2 测试系统,继承自BuildSystem""" + + def __init__(self): + super().__init__() + self.test_name = "test_config_physics" + self.test_exe = None + + def find_test_executable(self): + """查找测试可执行文件""" + possible_paths = [ + self.build_dir / "bin" / "Debug" / f"{self.test_name}.exe", + self.build_dir / "Debug" / f"{self.test_name}.exe", + self.build_dir / f"{self.test_name}.exe", + self.build_dir / "bin" / f"{self.test_name}.exe", + ] + + for path in possible_paths: + if path.exists(): + self.test_exe = path + self.print_success(f"Found test executable: {path}") + return True + + return False + + def run_test_with_intel_env(self): + """在Intel环境下运行测试""" + if not self.test_exe: + self.print_error("No test executable found") + return False + + self.print_step(1, 2, f"Running test: {self.test_exe.name}") + + try: + # 创建临时的批处理文件来运行测试(包含Intel环境) + import tempfile + + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + # 设置Intel环境 + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'echo [INFO] Intel environment configured\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'echo [WARNING] Intel environment not found\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + self.print_info(f"Command: {temp_bat}") + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower() or 'fail' in line.lower() or '✗' in line: + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or 'pass' in line.lower() or '✓' in line: + print(f" \033[92m{line}\033[0m") + elif '=' in line or '---' in line or '===' in line: + print(f" \033[96m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + self.print_warning("Test stderr output:") + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + self.print_step(2, 2, "Test execution completed") + + if result.returncode == 0: + self.print_success("Test passed") + return True + else: + self.print_error(f"Test failed (exit code: {result.returncode})") + return False + + except Exception as e: + self.print_error(f"Failed to run test: {e}") + return False + + def build_project(self, args): + """构建项目""" + if args.no_build: + self.print_info("Skipping build (--no-build flag)") + return True + + self.print_step(1, 3, "Building project") + + # 清理构建目录 + if args.clean and self.build_dir.exists(): + self.print_info("Cleaning build directory...") + import shutil + try: + shutil.rmtree(self.build_dir) + self.print_success("Build directory cleaned") + except Exception as e: + self.print_error(f"Clean failed: {e}") + if not args.force: + return False + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 配置CMake + self.print_step(2, 3, "Configuring CMake") + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-DCMAKE_BUILD_TYPE=Debug", + "-T", "fortran=ifx", + ] + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + if not self.run_command(cmake_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + # 构建项目 + self.print_step(3, 3, "Building project") + build_cmd = [ + "cmake", + "--build", ".", + "--config", "Debug", + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + if not self.run_command(build_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + self.print_success("Build completed") + return True + + def run(self): + """运行Step 2测试""" + import argparse + + parser = argparse.ArgumentParser( + description="Step 2: Configuration Physics Update", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认运行 + %(prog)s --clean # 清理后构建并测试 + %(prog)s --no-build # 只运行测试,不重新构建 + %(prog)s --verbose # 详细输出 + """ + ) + + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-build", action="store_true", + help="不重新构建,直接运行测试") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + + args = parser.parse_args() + + # 开始测试 + start_time = time.time() + + self.print_header("Step 2: Configuration Physics Update") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 设置Intel环境 + self.print_step(1, 4, "Setting up Intel oneAPI environment") + if not self.setup_intel_environment(args): + if not args.force: + self.print_error("Intel environment setup failed") + return 1 + self.print_warning("Intel environment setup failed, continuing...") + + # 2. 构建项目(如果需要) + self.print_step(2, 4, "Building project if needed") + if not self.build_project(args): + if not args.force: + return 1 + + # 3. 查找测试可执行文件 + self.print_step(3, 4, "Finding test executable") + if not self.find_test_executable(): + self.print_error(f"Test executable {self.test_name}.exe not found") + if not args.force: + return 1 + self.print_warning("Test executable not found, but continuing due to --force") + return 0 + + # 4. 运行测试 + self.print_step(4, 4, "Running configuration physics test") + test_passed = self.run_test_with_intel_env() + + # 生成报告 + test_time = time.time() - start_time + self.print_header("Step 2 Complete") + + print(f"测试: {'通过 ✓' if test_passed else '失败 ✗'}") + print(f"测试程序: {self.test_exe.name if self.test_exe else '未找到'}") + print(f"总耗时: {test_time:.1f}秒") + + if test_passed: + print(f"\n下一步: 更新组件管理器以支持物理模块") + print(f"建议: 修改component_manager.f90,添加physics组件创建") + return 0 + else: + if args.force: + self.print_warning("测试失败,但由于--flag继续执行") + return 0 + return 1 + + except KeyboardInterrupt: + self.print_error("\n测试被用户中断") + return 1 + except Exception as e: + self.print_error(f"测试过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + system = Step2System() + return system.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03e/src/CMakeLists.txt new file mode 100644 index 00000000..2d1466a7 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/CMakeLists.txt @@ -0,0 +1,39 @@ +# ==================== src\CMakeLists.txt ==================== + +# src/CMakeLists.txt +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 添加子目录 +add_subdirectory(base) +add_subdirectory(core) +add_subdirectory(infrastructure) +add_subdirectory(numerics) +add_subdirectory(physics) # ← 新增物理模块目录 +add_subdirectory(manager) +add_subdirectory(solver) + +# ==================== 新增:结果模块 ==================== +message(STATUS "配置结果模块...") + +add_library(results STATIC + results.f90 # 新增文件 +) + +target_link_libraries(results + PRIVATE + base + infrastructure + solver +) + +set_target_properties(results PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "结果模块配置完成") +# ==================== 结束新增 ==================== + +message(STATUS "源代码目录配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/base/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03e/src/base/CMakeLists.txt new file mode 100644 index 00000000..74f4aa65 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/base/CMakeLists.txt @@ -0,0 +1,16 @@ +# src/base/CMakeLists.txt +message(STATUS "Configuring base module...") + +add_library(base STATIC + modules.f90 + precision.f90 # 新增 +) + +set_target_properties(base PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Base module configured") + +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/base/modules.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/src/base/modules.f90 new file mode 100644 index 00000000..43aaee24 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/base/modules.f90 @@ -0,0 +1,36 @@ +! src/base/modules.f90 +module base_modules + use, intrinsic :: iso_fortran_env, only: real64, int32 + implicit none + + public :: wp, ip, max_name_len, string_len, cfd_config_base, component_info + + integer, parameter :: wp = real64 + integer, parameter :: ip = int32 + integer, parameter :: string_len = 100 + integer, parameter :: max_name_len = 32 + + ! 基础配置类型 + type :: cfd_config_base + character(len=max_name_len) :: ic_type = "step" + character(len=max_name_len) :: recon_scheme = "eno" + character(len=max_name_len) :: flux_type = "rusanov" + integer(ip) :: rk_order = 1 + real(wp) :: wave_speed = 1.0_wp + real(wp) :: final_time = 0.625_wp + real(wp) :: dt = 0.025_wp + character(len=max_name_len) :: boundary_type = "periodic" + integer(ip) :: spatial_order = 2 + character(len=max_name_len) :: equation_type = "linear_advection" + character(len=max_name_len) :: problem_type = "linear_advection" + logical :: verbose = .true. + end type cfd_config_base + + ! 组件信息类型 + type :: component_info + character(len=max_name_len) :: category = "" + character(len=max_name_len) :: name = "" + integer(ip) :: order = 0 + end type component_info + +end module base_modules \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/base/precision.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/src/base/precision.f90 new file mode 100644 index 00000000..4ac5fd7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/base/precision.f90 @@ -0,0 +1,9 @@ +! src/base/precision.f90(简单版本) +module precision_module + use base_modules, only: wp, ip + implicit none + + ! 重新导出,确保兼容 + public :: wp, ip + +end module precision_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03e/src/core/CMakeLists.txt new file mode 100644 index 00000000..d8b8df06 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/core/CMakeLists.txt @@ -0,0 +1,14 @@ +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") + +add_library(core STATIC + registry.f90 +) + +target_link_libraries(core PRIVATE base) + +set_target_properties(core PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Core module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/core/factory_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/src/core/factory_base.f90 new file mode 100644 index 00000000..302418a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/core/factory_base.f90 @@ -0,0 +1,57 @@ +! src/core/factory_base.f90 +module factory_base_module + use base_modules, only: wp, ip + use registry_module, only: create_component, has_component + + implicit none + private + public :: wp, ip, factory_base, factory_create + + ! 工厂基类 + type :: factory_base + character(len=max_name_length) :: category = "" + contains + procedure :: create => factory_base_create + procedure :: get_available => factory_base_get_available + end type factory_base + + ! 便捷函数类型 + abstract interface + function factory_function_interface(category, name) result(instance) + import :: wp + character(len=*), intent(in) :: category, name + class(*), allocatable :: instance + end function factory_function_interface + end interface + +contains + + ! 创建工厂实例 + function factory_create(category) result(factory) + character(len=*), intent(in) :: category + type(factory_base) :: factory + factory%category = trim(category) + end function factory_create + + ! 工厂创建方法 + function factory_base_create(this, name) result(instance) + class(factory_base), intent(in) :: this + character(len=*), intent(in) :: name + class(*), allocatable :: instance + + instance = create_component(this%category, name) + end function factory_base_create + + ! 获取可用组件列表(简化版) + subroutine factory_base_get_available(this, names, count) + class(factory_base), intent(in) :: this + character(len=*), allocatable, intent(out) :: names(:) + integer(ip), intent(out) :: count + + ! 这里需要实现从注册表获取列表的逻辑 + ! 暂时返回空列表 + count = 0 + allocate(character(len=max_name_length) :: names(0)) + end subroutine factory_base_get_available + +end module factory_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..a960167c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/src/core/registry.f90 new file mode 100644 index 00000000..d155aa19 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/core/registry.f90 @@ -0,0 +1,257 @@ +! src/core/registry.f90 (更新版) +module registry_module + use base_modules, only: wp, ip, max_name_len, component_info + + implicit none + private + + ! 明确公开所有需要的接口 + public :: wp, ip ! 类型参数 + public :: component_info ! 类型 + public :: registry_init, registry_cleanup ! 初始化/清理 + public :: register_component_simple ! 注册组件 + public :: has_component_simple ! 检查组件 + public :: list_components ! 列出组件 + public :: registry_is_initialized ! 检查初始化状态 + public :: registry_get_size ! 获取大小 + public :: initialize_default_components ! 新增:初始化默认组件 + + ! 全局注册表 + type :: component_registry + type(component_info), allocatable :: components(:) + integer(ip) :: count = 0 + integer(ip) :: capacity = 100 + logical :: initialized = .false. + logical :: verbose = .true. + logical :: default_components_added = .false. ! 新增:标记是否已添加默认组件 + end type component_registry + + type(component_registry) :: registry + +contains + + ! ==================== 公共API ==================== + + subroutine registry_init(verbose) + logical, optional, intent(in) :: verbose + + if (registry%initialized) then + if (registry%verbose) then + print *, "[REGISTRY] Already initialized" + end if + return + end if + + if (present(verbose)) then + registry%verbose = verbose + end if + + allocate(registry%components(registry%capacity)) + registry%initialized = .true. + + if (registry%verbose) then + print *, "[REGISTRY] Initialized with capacity:", registry%capacity + end if + end subroutine registry_init + + subroutine registry_cleanup() + if (allocated(registry%components)) then + deallocate(registry%components) + end if + registry%initialized = .false. + registry%count = 0 + registry%default_components_added = .false. ! 重置标记 + + if (registry%verbose) then + print *, "[REGISTRY] Cleaned up" + end if + end subroutine registry_cleanup + + ! 新增:初始化默认组件 + subroutine initialize_default_components() + if (.not. registry%initialized) then + call registry_init() + end if + + if (registry%default_components_added) then + if (registry%verbose) then + print *, "[REGISTRY] Default components already added" + end if + return + end if + + ! 注册重构器 + call register_component_simple("reconstructor", "eno", order=3) + call register_component_simple("reconstructor", "weno3", order=3) + call register_component_simple("reconstructor", "weno5", order=5) + + ! 注册通量计算器 + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + + ! 注册边界条件 + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + + ! 注册时间积分器 + call register_component_simple("integrator", "rk1", order=1) + call register_component_simple("integrator", "rk2", order=2) + call register_component_simple("integrator", "rk3", order=3) + + ! 注册方程 + call register_component_simple("equation", "linear_advection") + + ! 注册问题 + call register_component_simple("problem", "linear_advection") + + ! 注册初始条件 + call register_component_simple("initial_condition", "step") + call register_component_simple("initial_condition", "sin") + call register_component_simple("initial_condition", "gaussian") + + registry%default_components_added = .true. + + if (registry%verbose) then + print *, "[REGISTRY] Default components registered" + print *, "[REGISTRY] Total components:", registry%count + end if + end subroutine initialize_default_components + + subroutine register_component_simple(category, name, order) + character(len=*), intent(in) :: category, name + integer(ip), optional, intent(in) :: order + + integer(ip) :: i + type(component_info) :: info + + if (.not. registry%initialized) then + call registry_init() + end if + + ! 检查是否已存在 + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + if (registry%verbose) then + print *, "[WARN] Overwriting component: ", trim(category), ".", trim(name) + end if + + ! 更新 + if (present(order)) then + registry%components(i)%order = order + else + registry%components(i)%order = 0 + end if + return + end if + end do + + ! 扩展数组 + if (registry%count >= registry%capacity) then + call expand_registry() + end if + + ! 添加新组件 + registry%count = registry%count + 1 + + info%category = trim(category) + info%name = trim(name) + info%order = 0 + if (present(order)) then + info%order = order + end if + + registry%components(registry%count) = info + + if (registry%verbose) then + print *, "[OK] Registered simple: ", trim(category), ".", trim(name) + end if + end subroutine register_component_simple + + logical function has_component_simple(category, name) + character(len=*), intent(in) :: category, name + + integer(ip) :: i + + has_component_simple = .false. + + if (.not. registry%initialized) return + + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + has_component_simple = .true. + return + end if + end do + end function has_component_simple + + subroutine list_components(category) + character(len=*), optional, intent(in) :: category + + integer(ip) :: i, count + + if (.not. registry%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + if (registry%count == 0) then + print *, "[INFO] No components registered" + return + end if + + count = 0 + print *, "=== Registry Contents ===" + do i = 1, registry%count + if (.not. present(category) .or. & + trim(registry%components(i)%category) == trim(category)) then + call print_component_info(registry%components(i)) + count = count + 1 + end if + end do + + print *, "Total:", count, "components" + print *, "==========================" + end subroutine list_components + + ! ==================== 新增函数 ==================== + + logical function registry_is_initialized() + ! 检查注册表是否已初始化 + registry_is_initialized = registry%initialized + end function registry_is_initialized + + integer(ip) function registry_get_size() + ! 获取注册表中的组件数量 + registry_get_size = registry%count + end function registry_get_size + + ! ==================== 内部辅助函数 ==================== + + subroutine expand_registry() + type(component_info), allocatable :: temp(:) + + registry%capacity = registry%capacity * 2 + allocate(temp(registry%capacity)) + temp(1:registry%count) = registry%components(1:registry%count) + call move_alloc(temp, registry%components) + + if (registry%verbose) then + print *, "[INFO] Registry expanded to capacity:", registry%capacity + end if + end subroutine expand_registry + + subroutine print_component_info(info) + type(component_info), intent(in) :: info + + if (info%order > 0) then + print *, " [", trim(info%category), ".", trim(info%name), & + " (order:", info%order, ")]" + else + print *, " [", trim(info%category), ".", trim(info%name), "]" + end if + end subroutine print_component_info + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/core/registry_initializer.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/src/core/registry_initializer.f90 new file mode 100644 index 00000000..44023d1d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/core/registry_initializer.f90 @@ -0,0 +1,39 @@ +! src/core/registry_initializer.f90 (新增文件) +module registry_initializer_module + use registry_module, only: register_component_simple + implicit none + private + public :: initialize_default_registry + +contains + + subroutine initialize_default_registry() + ! 注册重构器 + call register_component_simple("reconstructor", "eno", order=3) + call register_component_simple("reconstructor", "weno3", order=3) + call register_component_simple("reconstructor", "weno5", order=5) + + ! 注册通量计算器 + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + + ! 注册边界条件 + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + + ! 注册时间积分器 + call register_component_simple("integrator", "rk1", order=1) + call register_component_simple("integrator", "rk2", order=2) + call register_component_simple("integrator", "rk3", order=3) + + ! 注册方程 + call register_component_simple("equation", "linear_advection") + + ! 注册问题 + call register_component_simple("problem", "linear_advection") + + print *, "[REGISTRY] Default components registered" + end subroutine initialize_default_registry + +end module registry_initializer_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03e/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..70cbbd2f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/infrastructure/CMakeLists.txt @@ -0,0 +1,17 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "Configuring infrastructure module...") + +add_library(infrastructure STATIC + config.f90 + mesh.f90 + domain.f90 # 新增 + solution.f90 # 新增 +) + +target_link_libraries(infrastructure PRIVATE base) + +set_target_properties(infrastructure PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Infrastructure module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/src/infrastructure/config.f90 new file mode 100644 index 00000000..7586a1a5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/infrastructure/config.f90 @@ -0,0 +1,144 @@ +! src/infrastructure/config.f90 (修复版) +module config_module + use base_modules, only: wp, ip, max_name_len, cfd_config_base + + implicit none + public :: wp, ip, cfd_config, config_print, config_with_reconstruction + + ! 扩展配置类型 - 添加物理相关字段 + type, extends(cfd_config_base) :: cfd_config + ! 物理参数 + real(wp) :: left_boundary_value = 1.0_wp + real(wp) :: right_boundary_value = 2.0_wp + real(wp) :: domain_length = 2.0_wp + + ! 新增:物理模块相关配置 + real(wp) :: pulse_center = 0.5_wp ! 高斯脉冲中心 + real(wp) :: pulse_width = 0.1_wp ! 高斯脉冲宽度 + logical :: enable_physics = .true. ! 是否启用物理模块 + contains + ! 新增:物理相关配置方法 + procedure :: set_physics_parameters + procedure :: get_physics_info + end type cfd_config + +contains + + subroutine config_print(cfg) + type(cfd_config), intent(in) :: cfg + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(cfg%ic_type) + print *, "Reconstruction: ", trim(cfg%recon_scheme), " (order:", cfg%spatial_order, ")" + print *, "Flux type: ", trim(cfg%flux_type) + print *, "Time integration: RK", cfg%rk_order + print *, "Wave speed: ", cfg%wave_speed + print *, "Final time: ", cfg%final_time + print *, "Time step: ", cfg%dt + print *, "Boundary: ", trim(cfg%boundary_type) + + ! 新增:物理配置信息 + print *, "--- Physics Configuration ---" + print *, "Equation type: ", trim(cfg%equation_type) + print *, "Problem type: ", trim(cfg%problem_type) + print *, "Domain length: ", cfg%domain_length + print *, "Physics enabled: ", cfg%enable_physics + + if (cfg%ic_type == "gaussian") then + print *, "Pulse center: ", cfg%pulse_center + print *, "Pulse width: ", cfg%pulse_width + end if + + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(cfg, scheme, order) + type(cfd_config), intent(inout) :: cfg + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + integer :: i + + ! 转换为小写 + cfg%recon_scheme = scheme + do i = 1, len_trim(cfg%recon_scheme) + if (cfg%recon_scheme(i:i) >= 'A' .and. cfg%recon_scheme(i:i) <= 'Z') then + cfg%recon_scheme(i:i) = char(ichar(cfg%recon_scheme(i:i)) + 32) + end if + end do + + ! 设置阶数 + if (present(order)) then + cfg%spatial_order = order + else + if (index(cfg%recon_scheme, 'weno') > 0) then + cfg%spatial_order = 5 + else if (trim(cfg%recon_scheme) == 'eno') then + cfg%spatial_order = 3 + end if + end if + + if (cfg%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(cfg%recon_scheme), & + " Order: ", cfg%spatial_order + end if + end subroutine config_with_reconstruction + + ! ========== 新增:物理参数设置方法 ========== + + subroutine set_physics_parameters(this, equation_type, problem_type, & + domain_length, enable_physics) + class(cfd_config), intent(inout) :: this + character(len=*), intent(in), optional :: equation_type, problem_type + real(wp), intent(in), optional :: domain_length + logical, intent(in), optional :: enable_physics + + if (present(equation_type)) then + this%equation_type = trim(equation_type) + if (this%verbose) then + print *, "[CONFIG] Set equation type: ", trim(this%equation_type) + end if + end if + + if (present(problem_type)) then + this%problem_type = trim(problem_type) + if (this%verbose) then + print *, "[CONFIG] Set problem type: ", trim(this%problem_type) + end if + end if + + if (present(domain_length)) then + this%domain_length = domain_length + if (this%verbose) then + print *, "[CONFIG] Set domain length: ", this%domain_length + end if + end if + + if (present(enable_physics)) then + this%enable_physics = enable_physics + if (this%verbose) then + print *, "[CONFIG] Physics module enabled: ", this%enable_physics + end if + end if + end subroutine set_physics_parameters + + subroutine get_physics_info(this) + class(cfd_config), intent(in) :: this + + print *, "=== Physics Configuration Info ===" + print *, "Equation type: ", trim(this%equation_type) + print *, "Problem type: ", trim(this%problem_type) + print *, "Domain length: ", this%domain_length + print *, "Wave speed: ", this%wave_speed + print *, "Physics enabled: ", this%enable_physics + + if (this%ic_type == "gaussian") then + print *, "Pulse parameters:" + print *, " Center: ", this%pulse_center + print *, " Width: ", this%pulse_width + end if + + print *, "==================================" + end subroutine get_physics_info + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/infrastructure/domain.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/src/infrastructure/domain.f90 new file mode 100644 index 00000000..c3662f03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/infrastructure/domain.f90 @@ -0,0 +1,102 @@ +! src/infrastructure/domain.f90 +module domain_module + use base_modules, only: wp, ip, max_name_len + use config_module, only: cfd_config + use mesh_module, only: mesh_type + + implicit none + private + public :: wp, ip, domain_type, domain_create, is_physical_cell + + type :: domain_type + type(cfd_config), pointer :: config => null() + type(mesh_type), pointer :: mesh => null() + integer(ip) :: nghosts = 0 + integer(ip) :: ist = 1 ! 物理区域起始索引(1-based) + integer(ip) :: ied = 1 ! 物理区域结束索引(exclusive) + integer(ip) :: ntcells = 0 ! 总单元数(含ghost) + contains + procedure :: print_info => domain_print_info + procedure :: get_physical_indices => domain_get_physical_indices + end type domain_type + +contains + + function domain_create(config, mesh) result(domain) + type(cfd_config), target, intent(in) :: config + type(mesh_type), target, intent(in) :: mesh + type(domain_type) :: domain + + domain%config => config + domain%mesh => mesh + + ! 计算ghost层数(参考Julia的_calc_nghosts) + domain%nghosts = calc_nghosts(config) + domain%ist = domain%nghosts + 1 + domain%ied = domain%ist + mesh%ncells + domain%ntcells = mesh%ncells + 2 * domain%nghosts + + if (config%verbose) then + print *, "[DOMAIN] Created:" + print *, " Ghost layers: ", domain%nghosts + print *, " Physical cells: ", domain%ist, " to ", domain%ied - 1 + print *, " Total cells: ", domain%ntcells + end if + end function domain_create + + function calc_nghosts(config) result(nghosts) + type(cfd_config), intent(in) :: config + integer(ip) :: nghosts + + character(len=max_name_len) :: scheme + + scheme = config%recon_scheme + + if (scheme == "eno") then + nghosts = config%spatial_order + else if (index(scheme, "weno") > 0) then + nghosts = config%spatial_order / 2 + 1 + else + print *, "[WARNING] Unknown scheme, using default nghosts=2" + nghosts = 2 + end if + + if (nghosts <= 0) then + print *, "[ERROR] Invalid nghosts: ", nghosts + nghosts = 2 + end if + end function calc_nghosts + + logical function is_physical_cell(this, idx) + class(domain_type), intent(in) :: this + integer(ip), intent(in) :: idx + is_physical_cell = (idx >= this%ist .and. idx < this%ied) + end function is_physical_cell + + function domain_get_physical_indices(this) result(indices) + class(domain_type), intent(in) :: this + integer(ip), allocatable :: indices(:) + integer(ip) :: i, count + + count = this%ied - this%ist + allocate(indices(count)) + + do i = 1, count + indices(i) = this%ist + i - 1 + end do + end function domain_get_physical_indices + + subroutine domain_print_info(this) + class(domain_type), intent(in) :: this + + print *, "=== Domain Information ===" + print *, "Configuration: ", trim(this%config%recon_scheme), & + " order ", this%config%spatial_order + print *, "Ghost layers: ", this%nghosts + print *, "Physical cells: ", this%ist, " to ", this%ied - 1 + print *, "Total cells: ", this%ntcells + print *, "Mesh cells: ", this%mesh%ncells + print *, "==========================" + end subroutine domain_print_info + +end module domain_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..f810f3a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/infrastructure/mesh.f90 @@ -0,0 +1,73 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use base_modules, only: wp, ip + + implicit none + public :: wp, ip, mesh_type, mesh_init, mesh_print_info + + ! 网格类型 + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer(ip) :: ncells = 40 + integer(ip) :: nnodes + integer(ip) :: nx + real(wp), allocatable :: x(:) ! 节点坐标 + real(wp), allocatable :: xcc(:) ! 单元中心坐标 + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer(ip), optional, intent(in) :: ncells + + integer(ip) :: i + + ! 设置参数 + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! 计算 + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! 分配内存 + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! 生成节点坐标 + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! 生成单元中心坐标 + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== Mesh Information ===" + print *, "Domain: [", this%xmin, ", ", this%xmax, "]" + print *, "Cells: ", this%ncells + print *, "Nodes: ", this%nnodes + print *, "dx: ", this%dx + print *, "L: ", this%L + print *, "========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/infrastructure/solution.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/src/infrastructure/solution.f90 new file mode 100644 index 00000000..ce88fd8a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/infrastructure/solution.f90 @@ -0,0 +1,131 @@ +! src/infrastructure/solution.f90 +module solution_module + use base_modules, only: wp, ip + use domain_module, only: domain_type + + implicit none + private + public :: wp, ip, solution_type, solution_create, solution_reset + + type :: solution_type + type(domain_type), pointer :: domain => null() + real(wp), allocatable :: u(:) ! 当前解(含ghost) + real(wp), allocatable :: un(:) ! 旧解 + real(wp), allocatable :: q_face_left(:) ! 左界面值 + real(wp), allocatable :: q_face_right(:)! 右界面值 + real(wp), allocatable :: flux(:) ! 通量 + real(wp), allocatable :: res(:) ! 残差 + contains + procedure :: initialize => solution_initialize + procedure :: update_old_field => solution_update_old_field + procedure :: print_info => solution_print_info + procedure :: reset => solution_reset_instance + end type solution_type + +contains + + function solution_create(domain) result(solution) + type(domain_type), target, intent(in) :: domain + type(solution_type) :: solution + + integer(ip) :: ncells, nnodes, ntcells + + solution%domain => domain + + ncells = domain%mesh%ncells + nnodes = domain%mesh%nnodes + ntcells = domain%ntcells + + ! 分配数组(与Julia solution.jl一致) + allocate(solution%u(ntcells), source=0.0_wp) + allocate(solution%un(ntcells), source=0.0_wp) + allocate(solution%q_face_left(nnodes), source=0.0_wp) + allocate(solution%q_face_right(nnodes), source=0.0_wp) + allocate(solution%flux(nnodes), source=0.0_wp) + allocate(solution%res(ncells), source=0.0_wp) + + if (domain%config%verbose) then + print *, "[SOLUTION] Created:" + print *, " u size: ", size(solution%u), " (with ghosts)" + print *, " flux size: ", size(solution%flux) + print *, " res size: ", size(solution%res) + end if + end function solution_create + + subroutine solution_initialize(this, initial_values) + class(solution_type), intent(inout) :: this + real(wp), intent(in), optional :: initial_values(:) + + integer(ip) :: i, idx + type(domain_type), pointer :: domain + + domain => this%domain + + if (present(initial_values)) then + ! 应用初始值到物理区域 + do i = domain%ist, domain%ied - 1 + idx = i - domain%ist + 1 + if (idx <= size(initial_values)) then + this%u(i) = initial_values(idx) + end if + end do + else + ! 默认为0 + this%u = 0.0_wp + end if + + ! 同步旧场(与Julia的update_old_field一致) + call this%update_old_field() + + if (domain%config%verbose) then + print *, "[SOLUTION] Initialized" + print *, " u range: ", minval(this%u), " to ", maxval(this%u) + end if + end subroutine solution_initialize + + subroutine solution_update_old_field(this) + class(solution_type), intent(inout) :: this + this%un = this%u ! 与Julia的 un .= u 一致 + end subroutine solution_update_old_field + + subroutine solution_reset_instance(this) + class(solution_type), intent(inout) :: this + call solution_reset(this) + end subroutine solution_reset_instance + + subroutine solution_reset(solution) + type(solution_type), intent(inout) :: solution + + if (allocated(solution%u)) solution%u = 0.0_wp + if (allocated(solution%un)) solution%un = 0.0_wp + if (allocated(solution%q_face_left)) solution%q_face_left = 0.0_wp + if (allocated(solution%q_face_right)) solution%q_face_right = 0.0_wp + if (allocated(solution%flux)) solution%flux = 0.0_wp + if (allocated(solution%res)) solution%res = 0.0_wp + + if (associated(solution%domain) .and. solution%domain%config%verbose) then + print *, "[SOLUTION] Reset" + end if + end subroutine solution_reset + + subroutine solution_print_info(this) + class(solution_type), intent(in) :: this + + print *, "=== Solution Information ===" + print *, "Arrays:" + print *, " u: ", size(this%u), " elements" + print *, " un: ", size(this%un), " elements" + print *, " q_face_left: ", size(this%q_face_left), " elements" + print *, " q_face_right: ", size(this%q_face_right), " elements" + print *, " flux: ", size(this%flux), " elements" + print *, " res: ", size(this%res), " elements" + + if (allocated(this%u)) then + print *, "Values:" + print *, " u min/max: ", minval(this%u), maxval(this%u) + print *, " un min/max: ", minval(this%un), maxval(this%un) + end if + print *, "============================" + end subroutine solution_print_info + +end module solution_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/manager/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03e/src/manager/CMakeLists.txt new file mode 100644 index 00000000..00c8bf49 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/manager/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/manager/CMakeLists.txt +message(STATUS "配置管理器模块...") + +# 创建管理器库 +add_library(manager STATIC + component_manager.f90 + component_factory.f90 +) + +# 明确依赖关系:管理器依赖所有其他模块 +target_link_libraries(manager + PRIVATE + core + infrastructure + reconstructor + flux +) + +# 设置模块输出目录 +set_target_properties(manager PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "管理器模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/manager/component_factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/src/manager/component_factory.f90 new file mode 100644 index 00000000..de8cbf1a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/manager/component_factory.f90 @@ -0,0 +1,142 @@ +! src/manager/component_factory.f90 (完整文件) +module component_factory_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + use eno_reconstructor_module, only: eno_reconstructor, create_eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor, create_weno3_reconstructor + use weno5_reconstructor_module, only: weno5_reconstructor, create_weno5_reconstructor + use rusanov_flux_module, only: rusanov_flux, create_rusanov_flux + + implicit none + private + public :: wp, create_reconstructor, create_flux_calculator + + ! 错误代码 + integer, parameter :: CM_SUCCESS = 0 + integer, parameter :: CM_ERROR_UNKNOWN_SCHEME = 1 + integer, parameter :: CM_ERROR_UNKNOWN_FLUX = 2 + integer, parameter :: CM_ERROR_INVALID_ORDER = 3 + +contains + + ! ==================== 重构器创建 ==================== + + function create_reconstructor(config, status) result(recon) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(reconstructor_base), allocatable :: recon + + character(len=20) :: scheme + integer :: order, error_code + + scheme = trim(adjustl(config%recon_scheme)) + order = config%spatial_order + + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating reconstructor: ", scheme, " order=", order + end if + + ! 处理"weno"作为WENO5的别名(与Julia一致) + if (scheme == "weno" .and. order == 5) then + scheme = "weno5" + end if + + select case(scheme) + case('eno') + allocate(eno_reconstructor :: recon) + select type(recon) + type is(eno_reconstructor) + recon = create_eno_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case('weno3') + allocate(weno3_reconstructor :: recon) + select type(recon) + type is(weno3_reconstructor) + recon = create_weno3_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case('weno5') + allocate(weno5_reconstructor :: recon) + select type(recon) + type is(weno5_reconstructor) + recon = create_weno5_reconstructor() + recon%order = order ! 覆盖默认阶数 + end select + + case default + error_code = CM_ERROR_UNKNOWN_SCHEME + if (config%verbose) then + print *, "[ERROR] Unknown reconstructor scheme: ", scheme + print *, " Available: eno, weno3, weno5" + end if + end select + + ! 检查阶数有效性 + if (error_code == CM_SUCCESS) then + if (order < 1) then + error_code = CM_ERROR_INVALID_ORDER + if (config%verbose) then + print *, "[ERROR] Invalid spatial order: ", order + end if + end if + end if + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Reconstructor creation failed" + end if + end function create_reconstructor + + ! ==================== 通量计算器创建 ==================== + + function create_flux_calculator(config, status) result(flux) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(flux_calculator_base), allocatable :: flux + + character(len=20) :: flux_type + integer :: error_code + + flux_type = trim(adjustl(config%flux_type)) + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating flux calculator: ", flux_type + end if + + select case(flux_type) + case('rusanov') + allocate(rusanov_flux :: flux) + select type(flux) + type is(rusanov_flux) + flux = create_rusanov_flux() + flux%wave_speed_default = config%wave_speed + end select + + case default + error_code = CM_ERROR_UNKNOWN_FLUX + if (config%verbose) then + print *, "[ERROR] Unknown flux type: ", flux_type + print *, " Available: rusanov" + end if + end select + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Flux calculator creation failed" + end if + end function create_flux_calculator + +end module component_factory_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/manager/component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/src/manager/component_manager.f90 new file mode 100644 index 00000000..25eac29b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/manager/component_manager.f90 @@ -0,0 +1,76 @@ +! src/manager/component_manager.f90 (完整文件) +module component_manager_module + use, intrinsic :: iso_fortran_env, only: wp => real64 + use config_module, only: cfd_config + use component_factory_module, only: create_reconstructor, create_flux_calculator + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + implicit none + private + public :: wp, component_manager_info, validate_config + public :: create_reconstructor, create_flux_calculator + +contains + + ! ==================== 配置验证 ==================== + + function validate_config(config) result(is_valid) + type(cfd_config), intent(in) :: config + logical :: is_valid + + integer :: status + class(reconstructor_base), allocatable :: test_recon + class(flux_calculator_base), allocatable :: test_flux + + is_valid = .false. + + ! 测试创建重构器 + test_recon = create_reconstructor(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid reconstructor configuration" + end if + return + end if + + ! 测试创建通量计算器 + test_flux = create_flux_calculator(config, status) + if (status /= 0) then + if (config%verbose) then + print *, "[CONFIG VALIDATION] Invalid flux configuration" + end if + return + end if + + ! 清理测试组件 + if (allocated(test_recon)) deallocate(test_recon) + if (allocated(test_flux)) deallocate(test_flux) + + is_valid = .true. + + if (config%verbose) then + print *, "[CONFIG VALIDATION] Configuration is valid" + end if + end function validate_config + + ! ==================== 信息显示 ==================== + + subroutine component_manager_info() + print *, "=== Component Manager ===" + print *, "Available reconstructors:" + print *, " - eno (orders: 1-7)" + print *, " - weno3 (order: 3)" + print *, " - weno5 (order: 5)" + print *, "" + print *, "Available flux calculators:" + print *, " - rusanov" + print *, "" + print *, "Features:" + print *, " - Configuration validation" + print *, " - Component creation from config" + print *, " - Error handling with status codes" + print *, "=========================" + end subroutine component_manager_info + +end module component_manager_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..3fde7746 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,28 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux STATIC + base.f90 + rusanov.f90 +) + +#target_link_libraries(flux PRIVATE core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 明确设置不使用 /Qm64 选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + set_target_properties(flux PROPERTIES + Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /Qm64" # 明确设置,避免继承问题 + ) +endif() + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/flux/base.f90 new file mode 100644 index 00000000..7080a7ae --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/flux/base.f90 @@ -0,0 +1,30 @@ +!src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + procedure :: print_basic_info => flux_print_basic ! 添加辅助方法 + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + + subroutine flux_print_basic(this) + class(flux_calculator_base), intent(in) :: this + print *, " Name: ", trim(this%name) + end subroutine flux_print_basic + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..daa9e3bb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/flux/rusanov.f90 @@ -0,0 +1,41 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux, create_rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + + ! 构造函数接口 + interface rusanov_flux + module procedure create_rusanov_flux + end interface + +contains + + ! 构造函数 + type(rusanov_flux) function create_rusanov_flux() result(this) + this%name = "Rusanov" + this%wave_speed_default = 1.0_real64 + end function create_rusanov_flux + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Flux calculator information:" + call this%print_basic_info() + + ! 添加Rusanov特有信息 + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..c88ea647 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,23 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor STATIC + base.f90 + eno.f90 + weno3.f90 + weno5.f90 +) + +target_link_libraries(reconstructor PRIVATE core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..53798d02 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/reconstructor/base.f90 @@ -0,0 +1,33 @@ +!src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + procedure :: print_basic_info => reconstructor_print_basic ! 添加一个辅助方法 + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + + subroutine reconstructor_print_basic(this) + class(reconstructor_base), intent(in) :: this + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_print_basic + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..f973e8b3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor, create_eno_reconstructor ! ← 添加这个 + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + + ! 构造函数接口 + interface eno_reconstructor + module procedure create_eno_reconstructor + end interface + +contains + + ! 构造函数 + type(eno_reconstructor) function create_eno_reconstructor() result(this) + this%name = "ENO" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_eno_reconstructor + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加ENO特有信息 + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..d5b7a747 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno3_reconstructor, create_weno3_reconstructor + + type, extends(reconstructor_base) :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + + ! 构造函数接口 + interface weno3_reconstructor + module procedure create_weno3_reconstructor + end interface + +contains + + ! 构造函数 + type(weno3_reconstructor) function create_weno3_reconstructor() result(this) + this%name = "WENO3" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_weno3_reconstructor + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO3特有信息 + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/reconstructor/weno5.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/reconstructor/weno5.f90 new file mode 100644 index 00000000..a869c67d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/numerics/reconstructor/weno5.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/weno5.f90(新增) +module weno5_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno5_reconstructor, create_weno5_reconstructor + + type, extends(reconstructor_base) :: weno5_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno5_info + end type weno5_reconstructor + + ! 构造函数接口 + interface weno5_reconstructor + module procedure create_weno5_reconstructor + end interface + +contains + + ! 构造函数 + type(weno5_reconstructor) function create_weno5_reconstructor() result(this) + this%name = "WENO5" + this%order = 5 + this%epsilon = 1.0e-6_real64 + end function create_weno5_reconstructor + + subroutine weno5_info(this) + class(weno5_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO5特有信息 + print *, " Type: WENO-5 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno5_info + +end module weno5_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/physics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03e/src/physics/CMakeLists.txt new file mode 100644 index 00000000..cc4e233a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/physics/CMakeLists.txt @@ -0,0 +1,19 @@ +# src/physics/CMakeLists.txt +message(STATUS "配置物理模块...") + +# 创建物理模块库 +add_library(physics STATIC + physics_interface.f90 + equations/linear_convection.f90 + problems/linear_convection_problem.f90 +) + +# 链接依赖 +target_link_libraries(physics PRIVATE base) + +# 设置模块输出目录 +set_target_properties(physics PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "物理模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/physics/equations/linear_convection.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/src/physics/equations/linear_convection.f90 new file mode 100644 index 00000000..fff7be55 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/physics/equations/linear_convection.f90 @@ -0,0 +1,49 @@ +! src/physics/equations/linear_convection.f90 +module linear_convection_equation + use precision_module, only: wp, ip + use physics_interface, only: physics_equation + implicit none + private + + ! 具体方程类型 - 先声明 + type, extends(physics_equation) :: linear_convection_eq + real(wp) :: wave_speed = 1.0_wp + contains + procedure :: flux => lc_flux + procedure :: speed => lc_speed + end type linear_convection_eq + + ! 公开接口 + public :: wp, ip + public :: linear_convection_eq, create_linear_convection_eq + +contains + + ! 构造函数 + function create_linear_convection_eq(wave_speed) result(eq) + real(wp), intent(in), optional :: wave_speed + type(linear_convection_eq) :: eq + + eq%name = "Linear Convection" + if (present(wave_speed)) then + eq%wave_speed = wave_speed + else + eq%wave_speed = 1.0_wp + end if + end function create_linear_convection_eq + + ! 方法实现 + pure function lc_flux(this, u) result(f) + class(linear_convection_eq), intent(in) :: this + real(wp), intent(in) :: u + real(wp) :: f + f = this%wave_speed * u + end function lc_flux + + pure function lc_speed(this) result(a) + class(linear_convection_eq), intent(in) :: this + real(wp) :: a + a = this%wave_speed + end function lc_speed + +end module linear_convection_equation \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/physics/physics_interface.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/src/physics/physics_interface.f90 new file mode 100644 index 00000000..45002da6 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/physics/physics_interface.f90 @@ -0,0 +1,64 @@ +! src/physics/physics_interface.f90 +module physics_interface + use precision_module, only: wp, ip + implicit none + private + + ! 定义抽象基类型 - 先声明为私有,然后在公开部分导出 + type, abstract :: physics_equation + character(len=:), allocatable :: name + contains + procedure(eq_flux_abs), deferred :: flux + procedure(eq_speed_abs), deferred :: speed + end type physics_equation + + type, abstract :: physics_problem + character(len=:), allocatable :: name + contains + procedure(prob_ic_abs), deferred :: initial_condition + procedure(prob_bc_abs), deferred :: boundary_condition + procedure(prob_exact_abs), deferred :: exact_solution + end type physics_problem + + ! 抽象接口定义 + abstract interface + pure function eq_flux_abs(this, u) result(f) + import :: physics_equation, wp + class(physics_equation), intent(in) :: this + real(wp), intent(in) :: u + real(wp) :: f + end function eq_flux_abs + + pure function eq_speed_abs(this) result(a) + import :: physics_equation, wp + class(physics_equation), intent(in) :: this + real(wp) :: a + end function eq_speed_abs + + subroutine prob_ic_abs(this, x, u) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp), intent(out) :: u(:) + end subroutine prob_ic_abs + + subroutine prob_bc_abs(this, u, t) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(inout) :: u(:) + real(wp), intent(in), optional :: t + end subroutine prob_bc_abs + + function prob_exact_abs(this, x, t) result(u) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(in) :: x(:), t + real(wp), dimension(size(x)) :: u + end function prob_exact_abs + end interface + + ! 公开接口 - 使用独立的public语句 + public :: wp, ip + public :: physics_equation, physics_problem + +end module physics_interface \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/physics/problems/linear_convection_problem.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/src/physics/problems/linear_convection_problem.f90 new file mode 100644 index 00000000..06226ed1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/physics/problems/linear_convection_problem.f90 @@ -0,0 +1,118 @@ +! src/physics/problems/linear_convection_problem.f90 +module linear_convection_problem + use precision_module, only: wp, ip + use physics_interface, only: physics_problem + implicit none + private + + ! 具体问题类型 - 先声明 + type, extends(physics_problem) :: linear_convection_prob + real(wp) :: wave_speed = 1.0_wp + real(wp) :: domain_length = 2.0_wp + character(len=20) :: ic_type = "step" + character(len=20) :: boundary_type = "periodic" + contains + procedure :: initial_condition => lc_initial_condition + procedure :: boundary_condition => lc_boundary_condition + procedure :: exact_solution => lc_exact_solution + end type linear_convection_prob + + ! 公开接口 + public :: wp, ip + public :: linear_convection_prob, create_linear_convection_prob + +contains + + ! 构造函数 + function create_linear_convection_prob(wave_speed, domain_length, & + ic_type, boundary_type) result(prob) + real(wp), intent(in), optional :: wave_speed, domain_length + character(len=*), intent(in), optional :: ic_type, boundary_type + type(linear_convection_prob) :: prob + + prob%name = "Linear Convection Problem" + + if (present(wave_speed)) prob%wave_speed = wave_speed + if (present(domain_length)) prob%domain_length = domain_length + if (present(ic_type)) prob%ic_type = ic_type + if (present(boundary_type)) prob%boundary_type = boundary_type + end function create_linear_convection_prob + + ! 初始条件 + subroutine lc_initial_condition(this, x, u) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp), intent(out) :: u(:) + + integer :: i + + select case (trim(this%ic_type)) + case ("step") + do i = 1, size(x) + if (x(i) >= 0.5_wp .and. x(i) <= 1.0_wp) then + u(i) = 2.0_wp + else + u(i) = 1.0_wp + end if + end do + + case ("sin", "sine") + do i = 1, size(x) + u(i) = sin(2.0_wp * 3.141592653589793_wp * x(i) / this%domain_length) + end do + + case ("gaussian") + do i = 1, size(x) + u(i) = exp(-((x(i) - 0.5_wp) / 0.1_wp)**2) + end do + + case default + ! 默认阶跃函数 + do i = 1, size(x) + if (x(i) >= 0.5_wp .and. x(i) <= 1.0_wp) then + u(i) = 2.0_wp + else + u(i) = 1.0_wp + end if + end do + end select + end subroutine lc_initial_condition + + ! 边界条件(虚拟实现,实际在boundary模块) + subroutine lc_boundary_condition(this, u, t) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(inout) :: u(:) + real(wp), intent(in), optional :: t + + ! 边界条件将在独立模块实现 + print *, "[PROBLEM] Boundary condition placeholder" + if (present(t)) then + print *, " Time = ", t + end if + end subroutine lc_boundary_condition + + ! 精确解(周期性平移) + function lc_exact_solution(this, x, t) result(u) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(in) :: x(:), t + real(wp), dimension(size(x)) :: u + real(wp), dimension(size(x)) :: x_shifted + integer :: i + + ! 周期性平移 + do i = 1, size(x) + x_shifted(i) = x(i) - this%wave_speed * t + ! 确保在 [0, domain_length) 范围内 + do while (x_shifted(i) < 0.0_wp) + x_shifted(i) = x_shifted(i) + this%domain_length + end do + do while (x_shifted(i) >= this%domain_length) + x_shifted(i) = x_shifted(i) - this%domain_length + end do + end do + + ! 重用初始条件函数 + call this%initial_condition(x_shifted, u) + end function lc_exact_solution + +end module linear_convection_problem \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/results.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/src/results.f90 new file mode 100644 index 00000000..f7ce0a7a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/results.f90 @@ -0,0 +1,290 @@ +! src/results.f90 (修正版) +module results_module + use base_modules, only: wp, ip + use config_module, only: cfd_config + use mesh_module, only: mesh_type + use domain_module, only: domain_type + use solution_module, only: solution_type + ! use physics_solver_module, only: physics_solver ! 暂时注释掉,避免循环依赖 + + implicit none + private + public :: results_saver, results_saver_create, save_results + + ! 定义字符串长度常量 + integer, parameter :: STR_LEN = 128 + + ! 结果类型 - 对应Julia的result字典 + type :: cfd_results + character(len=STR_LEN) :: solver_name = "" + real(wp), allocatable :: x(:) ! 网格坐标(单元中心) + real(wp), allocatable :: numerical(:) ! 数值解 + real(wp), allocatable :: analytical(:) ! 解析解 + character(len=STR_LEN) :: scheme = "" ! 格式名称 + integer :: order = 0 ! 阶数 + integer :: rk_order = 0 ! RK阶数 + real(wp) :: final_time = 0.0_wp ! 最终时间 + real(wp) :: current_time = 0.0_wp ! 当前时间 + integer :: total_steps = 0 ! 总步数 + integer :: solver_state = 0 ! 求解器状态 + end type cfd_results + + ! 结果保存器 + type :: results_saver + character(len=STR_LEN) :: base_filename = "results" + logical :: verbose = .true. + contains + procedure :: save_text => results_saver_save_text + procedure :: save_binary => results_saver_save_binary + procedure :: load => results_saver_load + end type results_saver + + ! 接口声明 + interface results_saver + module procedure results_saver_constructor + end interface + +contains + + ! 构造函数 + function results_saver_constructor(base_filename, verbose) result(saver) + character(len=*), optional :: base_filename + logical, optional :: verbose + type(results_saver) :: saver + + if (present(base_filename)) then + saver%base_filename = trim(adjustl(base_filename)) + end if + if (present(verbose)) then + saver%verbose = verbose + end if + end function results_saver_constructor + + ! 保持向后兼容的创建函数 + function results_saver_create(base_filename, verbose) result(saver) + character(len=*), optional :: base_filename + logical, optional :: verbose + type(results_saver) :: saver + + saver = results_saver_constructor(base_filename, verbose) + end function results_saver_create + + ! 生成文件名(与Julia风格一致) + function generate_filename(saver, solver_name, mesh_size) result(filename) + class(results_saver), intent(in) :: saver + character(len=*), intent(in) :: solver_name + integer, intent(in) :: mesh_size + character(len=STR_LEN) :: filename + + write(filename, '(A, "_", A, "_", I0, ".dat")') & + trim(saver%base_filename), trim(solver_name), mesh_size + end function generate_filename + + ! 主保存函数 - 生成与Julia兼容的结果 + subroutine save_results(saver, solver_name, config, mesh, domain, solution, & + current_time, total_steps, solver_state) + class(results_saver), intent(in) :: saver + character(len=*), intent(in) :: solver_name + type(cfd_config), intent(in) :: config + type(mesh_type), intent(in) :: mesh + type(domain_type), intent(in) :: domain + type(solution_type), intent(in) :: solution + real(wp), intent(in) :: current_time + integer, intent(in) :: total_steps, solver_state + + type(cfd_results) :: results + character(len=STR_LEN) :: filename + integer :: i, n_physical + + ! 准备结果数据 + results%solver_name = trim(solver_name) + results%scheme = trim(config%recon_scheme) + results%order = config%spatial_order + results%rk_order = config%rk_order + results%final_time = config%final_time + results%current_time = current_time + results%total_steps = total_steps + results%solver_state = solver_state + + ! 分配数组 + n_physical = mesh%ncells + allocate(results%x(n_physical)) + allocate(results%numerical(n_physical)) + allocate(results%analytical(n_physical)) + + ! 填充网格坐标(单元中心) + results%x = mesh%xcc + + ! 填充数值解(仅物理区域) + do i = 1, n_physical + results%numerical(i) = solution%u(domain%ist + i - 1) + end do + + ! 生成解析解(与Julia的exact_solution对应) + call generate_analytical_solution(results%x, config, results%analytical, current_time) + + ! 生成文件名 + filename = generate_filename(saver, solver_name, mesh%ncells) + + ! 保存文件 + if (saver%verbose) then + print *, "[RESULTS] Saving results to: ", trim(filename) + print *, " Solver: ", trim(results%solver_name) + print *, " Scheme: ", trim(results%scheme), " order ", results%order + print *, " Time: ", results%current_time, " / ", results%final_time + print *, " Steps: ", results%total_steps + end if + + call saver%save_text(results, filename) + + ! 清理 + deallocate(results%x, results%numerical, results%analytical) + end subroutine save_results + + ! 生成解析解(匹配Julia的exact_solution逻辑) + subroutine generate_analytical_solution(x, config, analytical, current_time) + real(wp), intent(in) :: x(:), current_time + type(cfd_config), intent(in) :: config + real(wp), intent(out) :: analytical(:) + + integer :: i, n + real(wp) :: x_shifted, L + + n = size(x) + L = config%domain_length + + select case (trim(config%ic_type)) + case ("step") + ! 阶跃函数的精确解(周期性) + do i = 1, n + ! 周期性平移 + x_shifted = x(i) - config%wave_speed * current_time + x_shifted = modulo(x_shifted, L) + if (x_shifted < 0.0_wp) x_shifted = x_shifted + L + + ! 阶跃在 [0.5, 1.0] 内为 2.0,其他为 1.0 + if (x_shifted >= 0.5_wp .and. x_shifted <= 1.0_wp) then + analytical(i) = 2.0_wp + else + analytical(i) = 1.0_wp + end if + end do + + case ("sin", "sine") + ! 正弦波的精确解 + do i = 1, n + x_shifted = x(i) - config%wave_speed * current_time + x_shifted = modulo(x_shifted, L) + analytical(i) = sin(2.0_wp * 3.141592653589793_wp * x_shifted / L) + end do + + case ("gaussian") + ! 高斯脉冲的精确解 + do i = 1, n + x_shifted = x(i) - config%wave_speed * current_time + x_shifted = modulo(x_shifted, L) + analytical(i) = exp(-50.0_wp * (x_shifted - 1.0_wp)**2) + end do + + case default + ! 默认:阶跃函数 + do i = 1, n + x_shifted = x(i) - config%wave_speed * current_time + x_shifted = modulo(x_shifted, L) + if (x_shifted >= 0.5_wp .and. x_shifted <= 1.0_wp) then + analytical(i) = 2.0_wp + else + analytical(i) = 1.0_wp + end if + end do + end select + end subroutine generate_analytical_solution + + ! 文本格式保存(与Julia的纯文本输出兼容) + subroutine results_saver_save_text(this, results, filename) + class(results_saver), intent(in) :: this + type(cfd_results), intent(in) :: results + character(len=*), intent(in) :: filename + + integer :: i, n, unit, ierr + + n = size(results%x) + + ! 打开文件 + open(newunit=unit, file=trim(filename), status='replace', & + action='write', iostat=ierr) + + if (ierr /= 0) then + if (this%verbose) then + print *, "[ERROR] Cannot open file: ", trim(filename) + end if + return + end if + + ! 写入头部信息(类似Julia的输出格式) + write(unit, '(A)') "========================================" + write(unit, '(A)') "CFD SOLVER RESULTS (Fortran)" + write(unit, '(A)') "========================================" + write(unit, '(A, A)') "Solver: ", trim(results%solver_name) + write(unit, '(A, A)') "Scheme: ", trim(results%scheme) + write(unit, '(A, I0)') "Order: ", results%order + write(unit, '(A, I0)') "RK Order: ", results%rk_order + write(unit, '(A, ES15.8)') "Final Time: ", results%final_time + write(unit, '(A, ES15.8)') "Current Time: ", results%current_time + write(unit, '(A, I0)') "Total Steps: ", results%total_steps + write(unit, '(A, I0)') "Solver State: ", results%solver_state + write(unit, '(A, I0)') "Grid Points: ", n + write(unit, '(A)') "========================================" + write(unit, '(A)') "DATA: x, numerical, analytical" + write(unit, '(A)') "========================================" + + ! 写入数据 + do i = 1, n + write(unit, '(3ES20.12)') results%x(i), results%numerical(i), results%analytical(i) + end do + + ! 关闭文件 + close(unit) + + if (this%verbose) then + print *, "[RESULTS] Saved ", n, " data points to ", trim(filename) + end if + end subroutine results_saver_save_text + + ! 二进制保存(可选) + subroutine results_saver_save_binary(this, results, filename) + class(results_saver), intent(in) :: this + type(cfd_results), intent(in) :: results + character(len=*), intent(in) :: filename + + ! 暂时实现文本格式,二进制格式可后续添加 + if (this%verbose) then + print *, "[INFO] Binary save not implemented, using text format" + end if + call this%save_text(results, filename) + end subroutine results_saver_save_binary + + ! 加载结果(暂时简单实现) + subroutine results_saver_load(this, filename, results) + class(results_saver), intent(in) :: this + character(len=*), intent(in) :: filename + type(cfd_results), intent(out) :: results + + ! 简化:只打印文件信息 + if (this%verbose) then + print *, "[RESULTS] Would load from: ", trim(filename) + print *, " Note: Load functionality needs implementation" + end if + + ! 初始化结果结构以避免未初始化警告 + results%solver_name = "" + results%scheme = "" + results%order = 0 + results%rk_order = 0 + results%final_time = 0.0_wp + results%current_time = 0.0_wp + results%total_steps = 0 + results%solver_state = 0 + end subroutine results_saver_load + +end module results_module diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/solver/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03e/src/solver/CMakeLists.txt new file mode 100644 index 00000000..ef6f72df --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/solver/CMakeLists.txt @@ -0,0 +1,22 @@ +# src/solver/CMakeLists.txt +message(STATUS "配置求解器模块...") + +add_library(solver STATIC + base.f90 + physics_solver.f90 +) + +target_link_libraries(solver + PRIVATE + infrastructure + core + physics + manager + results # ← 新增链接结果模块 +) + +set_target_properties(solver PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "求解器模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/solver/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/src/solver/base.f90 new file mode 100644 index 00000000..cfd78c47 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/solver/base.f90 @@ -0,0 +1,264 @@ +! src/solver/base.f90 +module solver_base_module + use base_modules, only: wp => wp, ip => ip ! 重命名以避免冲突 + + use config_module, only: cfd_config + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create + + implicit none + private + + ! 明确导出列表 + public :: wp, ip ! 类型参数 + public :: solver_base, create_solver_base ! 类型和构造函数 + public :: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, SOLVER_RUNNING + public :: SOLVER_COMPLETED, SOLVER_ERROR ! 状态常量 + + ! 求解器状态枚举 + integer, parameter :: SOLVER_UNINITIALIZED = 0 + integer, parameter :: SOLVER_INITIALIZED = 1 + integer, parameter :: SOLVER_RUNNING = 2 + integer, parameter :: SOLVER_COMPLETED = 3 + integer, parameter :: SOLVER_ERROR = 4 + + ! 求解器基类 + type :: solver_base + ! 基本组件 + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + + ! 状态管理 + integer :: state = SOLVER_UNINITIALIZED + character(len=100) :: error_message = "" + real(wp) :: current_time = 0.0_wp + integer(ip) :: current_step = 0 + + ! 时间控制 + real(wp) :: dt_original = 0.0_wp + contains + procedure :: initialize => solver_base_initialize + procedure :: step => solver_base_step + procedure :: run_to_time => solver_base_run_to_time + procedure :: cleanup => solver_base_cleanup + procedure :: get_state => solver_base_get_state + procedure :: get_error => solver_base_get_error + procedure :: print_info => solver_base_print_info + end type solver_base + + ! 构造函数接口 + interface solver_base + module procedure create_solver_base + end interface + +contains + + ! ==================== 构造函数 ==================== + + function create_solver_base(config, mesh) result(solver) + type(cfd_config), intent(in) :: config + type(mesh_type), intent(in) :: mesh + type(solver_base) :: solver + + solver%config = config + solver%mesh = mesh + + ! 创建域 + solver%domain = domain_create(config, mesh) + + ! 创建解 + solver%solution = solution_create(solver%domain) + + ! 保存原始时间步长 + solver%dt_original = config%dt + + if (config%verbose) then + print *, "[SOLVER] Base solver created" + print *, " Mesh cells: ", mesh%ncells + print *, " Domain total cells: ", solver%domain%ntcells + end if + end function create_solver_base + + ! ==================== 初始化 ==================== + + subroutine solver_base_initialize(this) + class(solver_base), intent(inout) :: this + + if (this%state == SOLVER_INITIALIZED) then + if (this%config%verbose) then + print *, "[SOLVER] Already initialized" + end if + return + end if + + ! 初始化解(通过配置) + ! 这里暂时简化,实际需要调用初始条件工厂 + print *, "[INFO] Base solver initialized (simplified)" + + ! 更新状态 + this%state = SOLVER_INITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + + if (this%config%verbose) then + print *, "[SOLVER] Initialized at t = ", this%current_time + end if + end subroutine solver_base_initialize + + ! ==================== 单步计算(虚方法) ==================== + + subroutine solver_base_step(this, dt) + class(solver_base), intent(inout) :: this + real(wp), intent(in) :: dt + + ! 基类中这只是虚方法,需要在子类中实现 + print *, "[INFO] Base solver step (virtual method)" + print *, " dt = ", dt + print *, " t = ", this%current_time + + ! 更新时间 + this%current_time = this%current_time + dt + this%current_step = this%current_step + 1 + + ! 简单模拟:只是更新状态 + if (this%config%verbose) then + print *, "[SOLVER] Step completed: t = ", this%current_time, & + ", step = ", this%current_step + end if + end subroutine solver_base_step + + ! ==================== 运行到指定时间 ==================== + + subroutine solver_base_run_to_time(this, final_time) + class(solver_base), intent(inout) :: this + real(wp), intent(in) :: final_time + + real(wp) :: dt, t_remaining + integer :: step_count + + if (this%state /= SOLVER_INITIALIZED) then + this%error_message = "Solver not initialized" + this%state = SOLVER_ERROR + if (this%config%verbose) then + print *, "[SOLVER BASE ERROR] Not initialized: ", trim(this%error_message) + end if + return + end if + + this%state = SOLVER_RUNNING + step_count = 0 + + if (this%config%verbose) then + print *, "[SOLVER BASE] Running from t = ", this%current_time, & + " to t = ", final_time + print *, " Time step: ", this%config%dt + end if + + do while (this%current_time < final_time - 1e-12_wp) + ! 计算时间步长 + t_remaining = final_time - this%current_time + dt = min(this%config%dt, t_remaining) + + ! 执行时间步 + call this%step(dt) + + step_count = step_count + 1 + + ! 每50步输出一次进度 + if (mod(step_count, 50) == 0 .and. this%config%verbose) then + print *, "[SOLVER BASE] Progress: t = ", this%current_time, & + " / ", final_time, " (step ", step_count, ")" + end if + end do + + ! 恢复原始时间步长 + this%config%dt = this%dt_original + + ! 更新状态 + this%state = SOLVER_COMPLETED + + if (this%config%verbose) then + print *, "[SOLVER BASE] Run completed:" + print *, " Final time: ", this%current_time + print *, " Total steps: ", this%current_step + print *, " State: ", this%state + end if + end subroutine solver_base_run_to_time + + ! ==================== 清理 ==================== + + subroutine solver_base_cleanup(this) + class(solver_base), intent(inout) :: this + + ! 重置状态 + this%state = SOLVER_UNINITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + this%error_message = "" + + if (this%config%verbose) then + print *, "[SOLVER] Cleaned up" + end if + end subroutine solver_base_cleanup + + ! ==================== 状态查询 ==================== + + function solver_base_get_state(this) result(state) + class(solver_base), intent(in) :: this + integer :: state + state = this%state + end function solver_base_get_state + + function solver_base_get_error(this) result(error_msg) + class(solver_base), intent(in) :: this + character(len=100) :: error_msg + error_msg = trim(this%error_message) + end function solver_base_get_error + + ! ==================== 信息打印 ==================== + + subroutine solver_base_print_info(this) + class(solver_base), intent(in) :: this + + character(len=20) :: state_str + + ! 状态字符串 + select case (this%state) + case (SOLVER_UNINITIALIZED) + state_str = "Uninitialized" + case (SOLVER_INITIALIZED) + state_str = "Initialized" + case (SOLVER_RUNNING) + state_str = "Running" + case (SOLVER_COMPLETED) + state_str = "Completed" + case (SOLVER_ERROR) + state_str = "Error" + case default + state_str = "Unknown" + end select + + print *, "=== Solver Information ===" + print *, "State: ", trim(state_str) + print *, "Current time: ", this%current_time + print *, "Current step: ", this%current_step + print *, "Error message: '", trim(this%error_message), "'" + + ! 配置信息 + print *, "Configuration:" + print *, " Scheme: ", trim(this%config%recon_scheme) + print *, " Order: ", this%config%spatial_order + print *, " dt: ", this%config%dt + + ! 域信息 + print *, "Domain:" + print *, " Ghost layers: ", this%domain%nghosts + print *, " Physical cells: ", this%domain%ist, " to ", this%domain%ied - 1 + + print *, "=========================" + end subroutine solver_base_print_info + +end module solver_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/src/solver/physics_solver.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/src/solver/physics_solver.f90 new file mode 100644 index 00000000..8701dc29 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/src/solver/physics_solver.f90 @@ -0,0 +1,518 @@ +! src/solver/physics_solver.f90 (简化版) +module physics_solver_module + use base_modules, only: wp => wp, ip => ip + use config_module, only: cfd_config + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create + + use physics_interface, only: physics_equation, physics_problem + use linear_convection_equation, only: linear_convection_eq, create_linear_convection_eq + use linear_convection_problem, only: linear_convection_prob, create_linear_convection_prob + + use component_manager_module, only: create_reconstructor, create_flux_calculator + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + implicit none + private + + ! 明确导出列表 + public :: wp, ip, physics_solver, create_physics_solver + public :: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, SOLVER_RUNNING + public :: SOLVER_COMPLETED, SOLVER_ERROR + + ! 求解器状态枚举 + integer, parameter :: SOLVER_UNINITIALIZED = 0 + integer, parameter :: SOLVER_INITIALIZED = 1 + integer, parameter :: SOLVER_RUNNING = 2 + integer, parameter :: SOLVER_COMPLETED = 3 + integer, parameter :: SOLVER_ERROR = 4 + + ! 物理求解器类型(不继承,独立实现) + type :: physics_solver + ! 基本组件 + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + + ! 物理组件 + class(physics_equation), allocatable :: equation + class(physics_problem), allocatable :: problem + + ! 数值组件 + class(reconstructor_base), allocatable :: reconstructor + class(flux_calculator_base), allocatable :: flux_calculator + + ! 状态管理 + integer :: state = SOLVER_UNINITIALIZED + character(len=100) :: error_message = "" + real(wp) :: current_time = 0.0_wp + integer(ip) :: current_step = 0 + + ! 时间控制 + real(wp) :: dt_original = 0.0_wp + logical :: physics_initialized = .false. + contains + procedure :: initialize => physics_solver_initialize + procedure :: step => physics_solver_step + procedure :: run_to_time => physics_solver_run_to_time + procedure :: cleanup => physics_solver_cleanup + procedure :: get_state => physics_solver_get_state + procedure :: get_error => physics_solver_get_error + procedure :: print_info => physics_solver_print_info + procedure, private :: create_physics_components + procedure, private :: create_numerical_components + end type physics_solver + + ! 构造函数接口 + interface physics_solver + module procedure create_physics_solver + end interface + +contains + + ! ==================== 构造函数 ==================== + + function create_physics_solver(config, mesh) result(solver) + type(cfd_config), intent(in) :: config + type(mesh_type), intent(in) :: mesh + type(physics_solver) :: solver + + solver%config = config + solver%mesh = mesh + + ! 创建域 + solver%domain = domain_create(config, mesh) + + ! 创建解 + solver%solution = solution_create(solver%domain) + + ! 保存原始时间步长 + solver%dt_original = config%dt + + ! 创建组件 + call solver%create_physics_components() + call solver%create_numerical_components() + + if (config%verbose) then + print *, "[PHYSICS SOLVER] Created:" + print *, " Mesh cells: ", mesh%ncells + print *, " Domain total cells: ", solver%domain%ntcells + end if + end function create_physics_solver + + + function get_physical_solution(this) result(u_physical) + class(physics_solver), intent(in) :: this + real(wp), allocatable :: u_physical(:) + + integer :: i, n_physical + + n_physical = this%mesh%ncells + allocate(u_physical(n_physical)) + + do i = 1, n_physical + u_physical(i) = this%solution%u(this%domain%ist + i - 1) + end do + end function get_physical_solution + + ! ==================== 创建物理组件 ==================== + + subroutine create_physics_components(this) + class(physics_solver), intent(inout) :: this + + ! 检查是否启用物理模块 + if (.not. this%config%enable_physics) then + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Physics module disabled" + end if + return + end if + + ! 创建物理方程 + select case (trim(this%config%equation_type)) + case ("linear_advection") + allocate(linear_convection_eq :: this%equation) + select type(eq => this%equation) + type is(linear_convection_eq) + eq = create_linear_convection_eq(wave_speed=this%config%wave_speed) + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Created linear convection equation" + print *, " Wave speed: ", eq%wave_speed + end if + end select + + case default + if (this%config%verbose) then + print *, "[WARNING] Unknown equation type: ", trim(this%config%equation_type) + print *, " Using linear convection as default" + end if + + allocate(linear_convection_eq :: this%equation) + select type(eq => this%equation) + type is(linear_convection_eq) + eq = create_linear_convection_eq(wave_speed=this%config%wave_speed) + end select + end select + + ! 创建物理问题 + select case (trim(this%config%problem_type)) + case ("linear_advection") + allocate(linear_convection_prob :: this%problem) + select type(prob => this%problem) + type is(linear_convection_prob) + prob = create_linear_convection_prob( & + wave_speed=this%config%wave_speed, & + domain_length=this%config%domain_length, & + ic_type=this%config%ic_type, & + boundary_type=this%config%boundary_type) + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Created linear convection problem" + print *, " IC type: ", trim(prob%ic_type) + end if + end select + + case default + if (this%config%verbose) then + print *, "[WARNING] Unknown problem type: ", trim(this%config%problem_type) + print *, " Using linear convection as default" + end if + + allocate(linear_convection_prob :: this%problem) + select type(prob => this%problem) + type is(linear_convection_prob) + prob = create_linear_convection_prob( & + wave_speed=this%config%wave_speed, & + domain_length=this%config%domain_length, & + ic_type=this%config%ic_type, & + boundary_type=this%config%boundary_type) + end select + end select + + this%physics_initialized = .true. + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Physics components created" + end if + end subroutine create_physics_components + + ! ==================== 创建数值组件 ==================== + + subroutine create_numerical_components(this) + class(physics_solver), intent(inout) :: this + integer :: status + + ! 创建重构器 + this%reconstructor = create_reconstructor(this%config, status) + if (status /= 0) then + print *, "[ERROR] Failed to create reconstructor" + this%state = SOLVER_ERROR + this%error_message = "Failed to create reconstructor" + return + end if + + ! 创建通量计算器 + this%flux_calculator = create_flux_calculator(this%config, status) + if (status /= 0) then + print *, "[ERROR] Failed to create flux calculator" + this%state = SOLVER_ERROR + this%error_message = "Failed to create flux calculator" + return + end if + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Numerical components created" + end if + end subroutine create_numerical_components + + ! ==================== 初始化 ==================== + + subroutine physics_solver_initialize(this) + class(physics_solver), intent(inout) :: this + + if (this%state == SOLVER_INITIALIZED) then + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Already initialized" + end if + return + end if + + ! 如果启用了物理模块,应用初始条件 + if (this%physics_initialized .and. allocated(this%problem)) then + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Applying initial condition" + end if + + select type(prob => this%problem) + type is(linear_convection_prob) + ! 获取网格单元中心坐标 + call prob%initial_condition(this%mesh%xcc, & + this%solution%u(this%domain%ist:this%domain%ied-1)) + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Initial condition applied" + end if + end select + else + ! 简化的初始化:阶跃函数 + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Using simplified initialization" + end if + + ! 在 [0.5, 1.0] 区域内设为 2.0,其他区域为 1.0 + where (this%mesh%xcc >= 0.5_wp .and. this%mesh%xcc <= 1.0_wp) + this%solution%u(this%domain%ist:this%domain%ied-1) = 2.0_wp + elsewhere + this%solution%u(this%domain%ist:this%domain%ied-1) = 1.0_wp + end where + end if + + ! 同步旧场 + call this%solution%update_old_field() + + ! 更新状态 + this%state = SOLVER_INITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Initialized at t = ", this%current_time + end if + end subroutine physics_solver_initialize + + ! ==================== 时间步进 ==================== + + subroutine physics_solver_step(this, dt) + class(physics_solver), intent(inout) :: this + real(wp), intent(in) :: dt + + integer :: i + real(wp) :: u_val, f_val + + if (this%config%verbose .and. mod(this%current_step, 100) == 0) then + print *, "[PHYSICS SOLVER] Step ", this%current_step + 1, & + " dt = ", dt, " t = ", this%current_time + end if + + ! 更新旧场 + call this%solution%update_old_field() + + ! 简化的数值方法 + do i = this%domain%ist, this%domain%ied - 1 + u_val = this%solution%un(i) ! 使用旧值 + + ! 简单的线性对流:u_t + a*u_x = 0 + ! 使用一阶迎风格式 + this%solution%u(i) = u_val - dt * this%config%wave_speed * & + (u_val - this%solution%un(i-1)) / this%mesh%dx + end do + + ! 更新时间 + this%current_time = this%current_time + dt + this%current_step = this%current_step + 1 + + ! 每100步输出一次进度 + if (this%config%verbose .and. mod(this%current_step, 100) == 0) then + print *, "[PHYSICS SOLVER] Step ", this%current_step, & + " completed, t = ", this%current_time + end if + end subroutine physics_solver_step + + ! ==================== 运行到指定时间 ==================== + + subroutine physics_solver_run_to_time(this, final_time) + class(physics_solver), intent(inout) :: this + real(wp), intent(in) :: final_time + + real(wp) :: dt, t_remaining + integer :: step_count + + if (this%state /= SOLVER_INITIALIZED) then + this%error_message = "Solver not initialized" + this%state = SOLVER_ERROR + if (this%config%verbose) then + print *, "[PHYSICS SOLVER ERROR] Not initialized" + end if + return + end if + + this%state = SOLVER_RUNNING + step_count = 0 + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Running from t = ", this%current_time, & + " to t = ", final_time + end if + + do while (this%current_time < final_time - 1e-12_wp) + ! 计算时间步长 + t_remaining = final_time - this%current_time + dt = min(this%config%dt, t_remaining) + + ! 执行时间步 + call this%step(dt) + + step_count = step_count + 1 + + ! 每100步输出一次进度 + if (mod(step_count, 100) == 0 .and. this%config%verbose) then + print *, "[PHYSICS SOLVER] Progress: t = ", this%current_time, & + " / ", final_time + end if + end do + + ! 恢复原始时间步长 + this%config%dt = this%dt_original + + ! 更新状态 + this%state = SOLVER_COMPLETED + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Run completed:" + print *, " Final time: ", this%current_time + print *, " Total steps: ", this%current_step + print *, " Final u range: ", minval(this%solution%u), " to ", maxval(this%solution%u) + end if + end subroutine physics_solver_run_to_time + + ! ==================== 清理 ==================== + + subroutine physics_solver_cleanup(this) + class(physics_solver), intent(inout) :: this + + integer :: old_state + real(wp) :: old_time + integer(ip) :: old_step + + ! 保存清理前的状态用于调试 + old_state = this%state + old_time = this%current_time + old_step = this%current_step + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Cleaning up..." + print *, " Before cleanup - State: ", old_state + print *, " Before cleanup - Time: ", old_time + print *, " Before cleanup - Steps: ", old_step + end if + + ! 清理物理组件 + if (allocated(this%equation)) then + deallocate(this%equation) + if (this%config%verbose) then + print *, " Deallocated equation" + end if + end if + + if (allocated(this%problem)) then + deallocate(this%problem) + if (this%config%verbose) then + print *, " Deallocated problem" + end if + end if + + ! 清理数值组件 + if (allocated(this%reconstructor)) then + deallocate(this%reconstructor) + if (this%config%verbose) then + print *, " Deallocated reconstructor" + end if + end if + + if (allocated(this%flux_calculator)) then + deallocate(this%flux_calculator) + if (this%config%verbose) then + print *, " Deallocated flux calculator" + end if + end if + + ! 重置状态 - 但不重置解数组(保持分配) + this%state = SOLVER_UNINITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + this%error_message = "" + this%physics_initialized = .false. + + if (this%config%verbose) then + print *, " After cleanup - State: ", this%state + print *, " After cleanup - Time: ", this%current_time + print *, " After cleanup - Steps: ", this%current_step + print *, "[PHYSICS SOLVER] Cleanup completed" + end if + + end subroutine physics_solver_cleanup + + ! ==================== 状态查询 ==================== + + function physics_solver_get_state(this) result(state) + class(physics_solver), intent(in) :: this + integer :: state + state = this%state + end function physics_solver_get_state + + function physics_solver_get_error(this) result(error_msg) + class(physics_solver), intent(in) :: this + character(len=100) :: error_msg + error_msg = trim(this%error_message) + end function physics_solver_get_error + + ! ==================== 信息打印 ==================== + + subroutine physics_solver_print_info(this) + class(physics_solver), intent(in) :: this + + character(len=20) :: state_str + + ! 状态字符串 + select case (this%state) + case (SOLVER_UNINITIALIZED) + state_str = "Uninitialized" + case (SOLVER_INITIALIZED) + state_str = "Initialized" + case (SOLVER_RUNNING) + state_str = "Running" + case (SOLVER_COMPLETED) + state_str = "Completed" + case (SOLVER_ERROR) + state_str = "Error" + case default + write(state_str, '(A, I3)') "Unknown ", this%state + end select + + print *, "=== Physics Solver Information ===" + print *, "State: ", trim(state_str), " (", this%state, ")" + print *, "Current time: ", this%current_time + print *, "Current step: ", this%current_step + print *, "Error message: '", trim(this%error_message), "'" + + ! 配置信息 + print *, "Configuration:" + print *, " Scheme: ", trim(this%config%recon_scheme) + print *, " Order: ", this%config%spatial_order + print *, " dt: ", this%config%dt + print *, " Final time: ", this%config%final_time + + ! 域信息 + print *, "Domain:" + print *, " Ghost layers: ", this%domain%nghosts + print *, " Physical cells: ", this%domain%ist, " to ", this%domain%ied - 1 + + ! 物理信息 + print *, "Physics:" + print *, " Initialized: ", this%physics_initialized + print *, " Equation type: ", trim(this%config%equation_type) + print *, " Problem type: ", trim(this%config%problem_type) + + ! 解信息 + if (allocated(this%solution%u)) then + print *, "Solution:" + print *, " u size: ", size(this%solution%u) + print *, " u physical size: ", this%domain%ied - this%domain%ist + end if + + print *, "===================================" + end subroutine physics_solver_print_info + +end module physics_solver_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/CMakeLists.txt new file mode 100644 index 00000000..b609e28d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/CMakeLists.txt @@ -0,0 +1,106 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +# 基础设施测试 +add_executable(test_infrastructure test_infrastructure.f90) +target_link_libraries(test_infrastructure + PRIVATE + infrastructure + core +) + +# 注册系统测试 +add_executable(test_registry test_registry.f90) +target_link_libraries(test_registry + PRIVATE + core + infrastructure +) + +# 物理模块测试 +add_executable(test_physics test_physics.f90) +target_link_libraries(test_physics + PRIVATE + physics + base +) + +# 组件管理器测试 +add_executable(test_component_manager test_component_manager.f90) +target_link_libraries(test_component_manager + PRIVATE + manager + infrastructure +) + +# 配置物理测试 +add_executable(test_config_physics test_config_physics.f90) +target_link_libraries(test_config_physics + PRIVATE + infrastructure + core +) + +# 求解器基础测试 +add_executable(test_solver_base test_solver_base.f90) +target_link_libraries(test_solver_base + PRIVATE + solver + infrastructure + core +) + +# 物理求解器测试 +add_executable(test_physics_solver test_physics_solver.f90) +target_link_libraries(test_physics_solver + PRIVATE + solver + infrastructure + core + physics + manager +) + +# 新增:简单物理求解器测试 +add_executable(test_physics_solver_simple test_physics_solver_simple.f90) +target_link_libraries(test_physics_solver_simple + PRIVATE + solver + infrastructure + core + physics + manager +) + +add_executable(test_simple_link test_simple_link.f90) +target_link_libraries(test_simple_link + PRIVATE + reconstructor + flux +) + + +add_executable(test_factory_simple test_factory_simple.f90) +target_link_libraries(test_factory_simple + PRIVATE + core + infrastructure + reconstructor + flux +) + +add_executable(test_domain_solution test_domain_solution.f90) +target_link_libraries(test_domain_solution + PRIVATE + infrastructure + core +) + +add_executable(test_component_manager_physics test_component_manager_physics.f90) +target_link_libraries(test_component_manager_physics + PRIVATE + manager + infrastructure + physics + core +) diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_cfd_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_cfd_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_cfd_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_component_manager.f90 new file mode 100644 index 00000000..f60c3505 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_component_manager.f90 @@ -0,0 +1,111 @@ +! tests/test_component_manager.f90 +program test_component_manager + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use component_manager_module, only: create_reconstructor, create_flux_calculator + use component_manager_module, only: component_manager_info, validate_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + implicit none + + type(cfd_config) :: config + class(reconstructor_base), allocatable :: recon + class(flux_calculator_base), allocatable :: flux + integer :: status + logical :: is_valid + + print *, "=== Component Manager Test ===" + print *, "" + + ! 显示组件管理器信息 + call component_manager_info() + print *, "" + + ! 测试1: 基本配置 + print *, "1. Testing basic ENO3 + Rusanov configuration..." + print *, "-----------------------------------------------" + + config%verbose = .true. + call config_print(config) + + ! 配置ENO3重构 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + call config_print(config) + print *, "" + + ! 验证配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Configuration is valid" + else + print *, "[ERROR] Configuration is invalid" + end if + print *, "" + + ! 测试2: 创建组件 + print *, "2. Testing component creation..." + print *, "--------------------------------" + + ! 创建重构器(带状态检查) + recon = create_reconstructor(config, status) + if (status == 0) then + print *, "[OK] Reconstructor created successfully" + call recon%info() + else + print *, "[ERROR] Failed to create reconstructor, code:", status + end if + print *, "" + + ! 创建通量计算器 + flux = create_flux_calculator(config, status) + if (status == 0) then + print *, "[OK] Flux calculator created successfully" + call flux%info() + else + print *, "[ERROR] Failed to create flux calculator, code:", status + end if + print *, "" + + ! 测试3: WENO3重构测试 + print *, "3. Testing WENO3 configuration..." + print *, "---------------------------------" + + call config_with_reconstruction(config, "weno3", 3) + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] WENO3 configuration is valid" + + ! 创建WENO3重构器 + recon = create_reconstructor(config) + call recon%info() + else + print *, "[ERROR] WENO3 configuration is invalid" + end if + print *, "" + + ! 测试4: 错误配置测试 + print *, "4. Testing invalid configuration..." + print *, "-----------------------------------" + + config%recon_scheme = "unknown_scheme" + config%flux_type = "unknown_flux" + + is_valid = validate_config(config) + if (.not. is_valid) then + print *, "[OK] Invalid configuration correctly rejected" + else + print *, "[ERROR] Invalid configuration should have been rejected" + end if + + ! 清理 + if (allocated(recon)) deallocate(recon) + if (allocated(flux)) deallocate(flux) + + print *, "" + print *, "=== Component manager test completed successfully ===" + +end program test_component_manager \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_component_manager_physics.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_component_manager_physics.f90 new file mode 100644 index 00000000..f2becca9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_component_manager_physics.f90 @@ -0,0 +1,120 @@ +! tests/test_component_manager_physics.f90 (简化版) +program test_component_manager_physics + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + use component_manager_module, only: component_manager_info, validate_config + + implicit none + + type(cfd_config) :: config + logical :: is_valid + + print *, "=== Component Manager Physics Test (Simplified) ===" + print *, "" + + ! 测试1: 显示组件管理器信息 + print *, "1. Testing component manager info..." + print *, "-------------------------------------" + call component_manager_info() + print *, "" + + ! 测试2: 物理模块测试(默认) + print *, "2. Testing physics module with default configuration..." + print *, "------------------------------------------------------" + + config%verbose = .true. + call config_print(config) + + ! 验证配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Default configuration is valid" + else + print *, "[ERROR] Default configuration is invalid" + end if + print *, "" + + ! 测试3: 测试物理配置 + print *, "3. Testing physics configuration..." + print *, "------------------------------------" + + ! 修改物理参数 + config%equation_type = "linear_advection" + config%problem_type = "linear_advection" + config%wave_speed = 2.5_wp + config%domain_length = 3.0_wp + + print *, "Modified physics configuration:" + print *, " Equation type: ", trim(config%equation_type) + print *, " Problem type: ", trim(config%problem_type) + print *, " Wave speed: ", config%wave_speed + print *, " Domain length: ", config%domain_length + print *, " Physics enabled: ", config%enable_physics + + ! 验证修改后的配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Modified physics configuration is valid" + else + print *, "[ERROR] Modified physics configuration is invalid" + end if + print *, "" + + ! 测试4: 数值组件测试 + print *, "4. Testing numerical components with physics..." + print *, "-----------------------------------------------" + + call config_with_reconstruction(config, "weno3", 3) + config%flux_type = "rusanov" + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Combined physics+numerics configuration is valid" + else + print *, "[ERROR] Combined configuration is invalid" + end if + print *, "" + + ! 测试5: 物理模块禁用测试 + print *, "5. Testing physics module disabled..." + print *, "---------------------------------------" + + config%enable_physics = .false. + config%verbose = .false. + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Configuration valid even with physics disabled" + else + print *, "[ERROR] Configuration should be valid with physics disabled" + end if + print *, "" + + ! 测试6: 错误配置测试 + print *, "6. Testing error handling..." + print *, "-----------------------------" + + config%verbose = .true. + config%enable_physics = .true. + config%recon_scheme = "unknown_scheme" + config%flux_type = "unknown_flux" + config%equation_type = "unknown_equation" + config%problem_type = "unknown_problem" + + is_valid = validate_config(config) + if (.not. is_valid) then + print *, "[OK] Invalid configuration correctly rejected" + else + print *, "[ERROR] Invalid configuration should have been rejected" + end if + print *, "" + + print *, "=== Component Manager Physics Test Summary ===" + print *, "✓ Component manager info works" + print *, "✓ Configuration validation works with physics" + print *, "✓ Error handling works correctly" + print *, "✓ Combined physics+numerics validation works" + print *, "" + print *, "下一步: 集成物理模块到求解器框架" + +end program test_component_manager_physics \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_config_physics.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_config_physics.f90 new file mode 100644 index 00000000..c6fef5c0 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_config_physics.f90 @@ -0,0 +1,141 @@ +! tests/test_config_physics.f90 (修复版) +program test_config_physics + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + + implicit none + + type(cfd_config) :: config + + print *, "=== Configuration Physics Test (Simplified) ===" + print *, "" + + ! 测试1: 默认配置 + print *, "1. Testing default configuration..." + print *, "-----------------------------------" + call config_print(config) + print *, "" + + ! 测试2: 验证基础物理字段 + print *, "2. Testing basic physics fields..." + print *, "----------------------------------" + + print *, "Verifying default physics fields:" + + if (trim(config%equation_type) == "linear_advection") then + print *, " ✓ Default equation type: linear_advection" + else + print *, " ✗ Unexpected equation type: ", trim(config%equation_type) + end if + + if (trim(config%problem_type) == "linear_advection") then + print *, " ✓ Default problem type: linear_advection" + else + print *, " ✗ Unexpected problem type: ", trim(config%problem_type) + end if + + if (abs(config%domain_length - 2.0_wp) < 1e-10_wp) then + print *, " ✓ Default domain length: 2.0" + else + print *, " ✗ Unexpected domain length: ", config%domain_length + end if + + if (config%enable_physics) then + print *, " ✓ Physics enabled by default" + else + print *, " ✗ Physics not enabled by default" + end if + + print *, "" + + ! 测试3: 使用类型绑定的方法(正确的方法名) + print *, "3. Testing type-bound procedures..." + print *, "--------------------------------------" + + call config%set_physics_parameters( & + equation_type="burgers_equation", & + problem_type="sod_shock_tube", & + domain_length=3.0_wp, & + enable_physics=.false.) + + print *, "After set_physics_parameters:" + print *, " Equation type: ", trim(config%equation_type) + print *, " Problem type: ", trim(config%problem_type) + print *, " Domain length: ", config%domain_length + print *, " Physics enabled: ", config%enable_physics + + if (trim(config%equation_type) == "burgers_equation") then + print *, " ✓ Equation type modified successfully via set_physics_parameters" + end if + + if (trim(config%problem_type) == "sod_shock_tube") then + print *, " ✓ Problem type modified successfully via set_physics_parameters" + end if + + if (abs(config%domain_length - 3.0_wp) < 1e-10_wp) then + print *, " ✓ Domain length modified successfully via set_physics_parameters" + end if + + if (.not. config%enable_physics) then + print *, " ✓ Physics disabled successfully via set_physics_parameters" + end if + + print *, "" + + ! 测试4: 调用get_physics_info方法 + print *, "4. Testing get_physics_info method..." + print *, "--------------------------------------" + call config%get_physics_info() + print *, "" + + ! 测试5: 高斯脉冲配置 + print *, "5. Testing Gaussian pulse configuration..." + print *, "-----------------------------------------" + + config%ic_type = "gaussian" + config%pulse_center = 0.6_wp + config%pulse_width = 0.15_wp + + print *, "Gaussian pulse parameters:" + print *, " IC type: ", trim(config%ic_type) + print *, " Center: ", config%pulse_center + print *, " Width: ", config%pulse_width + + if (trim(config%ic_type) == "gaussian") then + print *, " ✓ Gaussian IC type set" + end if + + if (abs(config%pulse_center - 0.6_wp) < 1e-10_wp) then + print *, " ✓ Pulse center set" + end if + + if (abs(config%pulse_width - 0.15_wp) < 1e-10_wp) then + print *, " ✓ Pulse width set" + end if + + print *, "" + + ! 测试6: 重构配置 + print *, "6. Testing reconstruction configuration..." + print *, "------------------------------------------" + + call config_with_reconstruction(config, "weno", 5) + + print *, "Reconstruction configuration:" + print *, " Scheme: ", trim(config%recon_scheme) + print *, " Order: ", config%spatial_order + + if (trim(config%recon_scheme) == "weno" .and. config%spatial_order == 5) then + print *, " ✓ WENO5 configuration successful" + else + print *, " ✗ Reconstruction configuration failed" + end if + + print *, "" + + print *, "=== Configuration Physics Test Complete ===" + print *, "✓ Config module updated with physics support" + print *, "✓ Fields can be directly accessed and modified" + print *, "✓ Type-bound procedures work correctly" + +end program test_config_physics \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_domain_solution.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_domain_solution.f90 new file mode 100644 index 00000000..ff659bac --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_domain_solution.f90 @@ -0,0 +1,102 @@ +! tests/test_domain_solution.f90 +program test_domain_solution + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create, solution_reset + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + real(wp), allocatable :: initial_values(:) + integer :: i + + print *, "=== Domain and Solution Test ===" + print *, "" + + ! 测试1: 不同重构方案的ghost层计算 + print *, "1. Testing ghost layer calculation..." + print *, "--------------------------------------" + + ! ENO3 + call config_with_reconstruction(config, "eno", 3) + config%verbose = .false. + call mesh%init(ncells=10) + domain = domain_create(config, mesh) + print *, "ENO3: nghosts = ", domain%nghosts, " (expected: 3)" + + ! WENO3 + call config_with_reconstruction(config, "weno3", 3) + domain = domain_create(config, mesh) + print *, "WENO3: nghosts = ", domain%nghosts, " (expected: 2)" + + ! WENO5 + call config_with_reconstruction(config, "weno", 5) + domain = domain_create(config, mesh) + print *, "WENO5: nghosts = ", domain%nghosts, " (expected: 3)" + print *, "" + + ! 测试2: Solution数组 + print *, "2. Testing solution arrays..." + print *, "------------------------------" + + call config_with_reconstruction(config, "eno", 3) + config%verbose = .true. + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + domain = domain_create(config, mesh) + call domain%print_info() + print *, "" + + solution = solution_create(domain) + call solution%print_info() + print *, "" + + ! 测试3: 初始化和更新 + print *, "3. Testing initialization and update..." + print *, "----------------------------------------" + + allocate(initial_values(mesh%ncells)) + do i = 1, mesh%ncells + initial_values(i) = sin(2.0_wp * 3.14159265358979_wp * mesh%xcc(i) / mesh%L) + end do + + call solution%initialize(initial_values) + print *, "After initialization:" + print *, " u range: ", minval(solution%u), " to ", maxval(solution%u) + print *, " un range: ", minval(solution%un), " to ", maxval(solution%un) + + ! 修改当前解,测试更新 + solution%u = solution%u * 2.0_wp + call solution%update_old_field() + print *, "After update: max|u - un| = ", maxval(abs(solution%u - solution%un)) + print *, "" + + ! 测试4: 重置 + print *, "4. Testing reset..." + print *, "-------------------" + + call solution_reset(solution) + print *, "After reset:" + print *, " u max: ", maxval(abs(solution%u)) + print *, " un max: ", maxval(abs(solution%un)) + print *, " flux max: ", maxval(abs(solution%flux)) + print *, "" + + deallocate(initial_values) + + print *, "=== Test Summary ===" + print *, "✓ Ghost layer calculation works" + print *, "✓ Domain creation works" + print *, "✓ Solution arrays work" + print *, "✓ Initialization works" + print *, "✓ Field update works" + print *, "✓ Reset works" + print *, "" + print *, "Ready for next step: Implementing Physics modules" + +end program test_domain_solution \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_factory_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_factory_simple.f90 new file mode 100644 index 00000000..db65da7c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_factory_simple.f90 @@ -0,0 +1,58 @@ +! tests/test_factory_simple.f90 (修复版) +program test_factory_simple + use base_modules, only: wp ! ← 添加这行 + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Basic systems + print *, "1. Testing basic systems..." + print *, "-----------------------------" + call config_print(config) + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Creating reconstructors + print *, "2. Testing reconstructors..." + print *, "------------------------------" + + ! 创建并测试ENO重构器 + print *, "Creating ENO reconstructor..." + eno = eno_reconstructor() ! 使用构造函数 + call eno%info() ! 必须调用info方法 + + print *, "" + print *, "Creating WENO3 reconstructor..." + weno3 = weno3_reconstructor() ! 使用构造函数 + call weno3%info() ! 必须调用info方法 + print *, "" + + ! Test 3: Creating flux calculator + print *, "3. Testing flux calculator..." + print *, "-------------------------------" + + print *, "Creating Rusanov flux calculator..." + rusanov = rusanov_flux() ! 使用构造函数 + call rusanov%info() ! 必须调用info方法 + print *, "" + + print *, "=== Factory pattern simple test completed successfully ===" + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_infrastructure.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_infrastructure.f90 new file mode 100644 index 00000000..22fa92d1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_infrastructure.f90 @@ -0,0 +1,56 @@ +! tests/test_infrastructure.f90 (原test_basic_only.f90) +program test_infrastructure + use base_modules, only: wp + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use registry_module, only: registry_init, registry_cleanup, & + register_component_simple, list_components + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "=== 基础设施测试 ===" + print *, "" + + ! 测试1: 配置 + print *, "1. 测试配置模块..." + print *, "-------------------" + call config_print(config) + print *, "" + + ! 测试2: 网格 + print *, "2. 测试网格模块..." + print *, "------------------" + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=5) + print *, "网格初始化:" + print *, " 单元数: ", mesh%ncells + print *, " 节点数: ", mesh%nnodes + print *, " 网格间距: ", mesh%dx + print *, "" + + ! 测试3: 注册系统 + print *, "3. 测试注册系统..." + print *, "------------------" + + call registry_init() + + ! 注册组件(使用简化版本) + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! 列出组件 + call list_components() + print *, "" + + ! 清理 + call registry_cleanup() + + print *, "=== 基础设施测试通过 ===" + print *, "✓ 配置模块工作正常" + print *, "✓ 网格模块工作正常" + print *, "✓ 注册系统工作正常" + +end program test_infrastructure \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_physics.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_physics.f90 new file mode 100644 index 00000000..3dababb6 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_physics.f90 @@ -0,0 +1,91 @@ +! tests/test_physics.f90 (原test_physics_minimal.f90) +program test_physics + use base_modules, only: wp, ip + use linear_convection_equation, only: linear_convection_eq, create_linear_convection_eq + use linear_convection_problem, only: linear_convection_prob, create_linear_convection_prob + + implicit none + + type(linear_convection_eq) :: eq + type(linear_convection_prob) :: prob + real(wp) :: u, f, a + real(wp), allocatable :: x(:), u_ic(:), u_exact(:) + integer :: i, nx = 10 + + print *, "=== 物理模块基础测试 ===" + print *, "" + + ! 测试1: 方程功能 + print *, "1. 测试方程功能..." + print *, "-------------------" + + eq = create_linear_convection_eq(wave_speed=2.0_wp) + print *, "方程: ", eq%name + print *, "波速: ", eq%wave_speed + + u = 1.5_wp + f = eq%flux(u) + a = eq%speed() + + print *, "u = ", u + print *, "F(u) = ", f, " (期望: 3.0)" + print *, "波速 a = ", a, " (期望: 2.0)" + + if (abs(f - 3.0_wp) < 1e-10_wp .and. abs(a - 2.0_wp) < 1e-10_wp) then + print *, "✓ 方程功能正常" + else + print *, "✗ 方程功能异常" + end if + print *, "" + + ! 测试2: 问题功能 + print *, "2. 测试问题功能..." + print *, "-------------------" + + prob = create_linear_convection_prob(ic_type="step", domain_length=2.0_wp) + print *, "问题: ", prob%name + print *, "初始条件类型: ", trim(prob%ic_type) + print *, "域长度: ", prob%domain_length + + allocate(x(nx), u_ic(nx), u_exact(nx)) + do i = 1, nx + x(i) = 0.0_wp + (i-1) * 0.2_wp + end do + + ! 测试初始条件 + call prob%initial_condition(x, u_ic) + print *, "初始条件范围: ", minval(u_ic), " 到 ", maxval(u_ic) + + ! 测试精确解 + u_exact = prob%exact_solution(x, 0.0_wp) + print *, "t=0时精确解范围: ", minval(u_exact), " 到 ", maxval(u_exact) + + ! 检查阶跃函数 + if (abs(u_ic(1) - 1.0_wp) < 1e-10_wp .and. & + abs(u_ic(6) - 2.0_wp) < 1e-10_wp) then + print *, "✓ 阶跃初始条件正确" + else + print *, "✗ 阶跃初始条件错误" + end if + + ! 检查精确解与初始条件一致 + if (maxval(abs(u_ic - u_exact)) < 1e-10_wp) then + print *, "✓ t=0时精确解与初始条件一致" + else + print *, "✗ 精确解计算错误" + end if + print *, "" + + ! 测试3: 边界条件接口 + print *, "3. 测试边界条件接口..." + print *, "----------------------" + call prob%boundary_condition(u_ic, 0.0_wp) + print *, "✓ 边界条件接口正常" + print *, "" + + deallocate(x, u_ic, u_exact) + + print *, "=== 物理模块测试完成 ===" + print *, "下一步: 将物理模块集成到现有系统中" + +end program test_physics \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_physics_solver.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_physics_solver.f90 new file mode 100644 index 00000000..ba49ffba --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_physics_solver.f90 @@ -0,0 +1,133 @@ +! tests/test_physics_solver.f90 (修复版) +program test_physics_solver + use base_modules, only: wp ! 使用一致的wp定义 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use mesh_module, only: mesh_type + use solver_base_module, only: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, & + SOLVER_COMPLETED, SOLVER_ERROR + use physics_solver_module, only: physics_solver + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(physics_solver) :: psolver + character(len=100) :: error_msg + integer :: state + + print *, "=== Physics Solver Test ===" + print *, "" + + ! 测试1: 创建物理求解器(默认物理配置) + print *, "1. Creating physics solver (default physics)..." + print *, "------------------------------------------------" + + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%dt = 0.01_wp + config%enable_physics = .true. + + call config_print(config) + + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + psolver = physics_solver(config, mesh) + call psolver%print_info() + print *, "" + + ! 测试2: 初始化 + print *, "2. Initializing physics solver..." + print *, "----------------------------------" + + call psolver%initialize() + state = psolver%get_state() + error_msg = psolver%get_error() + print *, "State after initialization: ", state + print *, "Expected: ", SOLVER_INITIALIZED + print *, "Match? ", state == SOLVER_INITIALIZED + print *, "Error message: '", trim(error_msg), "'" + print *, "" + + ! 测试3: 运行一小段时间 + print *, "3. Running physics solver (short time)..." + print *, "------------------------------------------" + + call psolver%run_to_time(0.02_wp) + state = psolver%get_state() + error_msg = psolver%get_error() + print *, "State after short run: ", state + print *, "Expected: ", SOLVER_COMPLETED + print *, "Match? ", state == SOLVER_COMPLETED + print *, "Current time: ", psolver%current_time + print *, "Current step: ", psolver%current_step + print *, "" + + ! 测试4: 禁用物理模块 + print *, "4. Testing physics solver with physics disabled..." + print *, "--------------------------------------------------" + + config%enable_physics = .false. + config%verbose = .false. + + psolver = physics_solver(config, mesh) + call psolver%initialize() + call psolver%run_to_time(0.01_wp) + + state = psolver%get_state() + error_msg = psolver%get_error() + print *, "State with physics disabled: ", state + print *, "Expected: ", SOLVER_COMPLETED + print *, "Match? ", state == SOLVER_COMPLETED + print *, "" + + ! 测试5: 不同物理配置 + print *, "5. Testing different physics configurations..." + print *, "----------------------------------------------" + + config%verbose = .true. + config%enable_physics = .true. + config%equation_type = "linear_advection" + config%problem_type = "linear_advection" + config%wave_speed = 2.5_wp + config%domain_length = 3.0_wp + config%ic_type = "gaussian" + + psolver = physics_solver(config, mesh) + call psolver%initialize() + + print *, "Physics configuration test completed" + print *, "" + + ! 测试6: 清理和错误处理 + print *, "6. Testing cleanup and error handling..." + print *, "----------------------------------------" + + call psolver%cleanup() + state = psolver%get_state() + error_msg = psolver%get_error() + print *, "State after cleanup: ", state + print *, "Expected: ", SOLVER_UNINITIALIZED + print *, "Match? ", state == SOLVER_UNINITIALIZED + + ! 尝试运行已清理的求解器 + call psolver%run_to_time(0.01_wp) + state = psolver%get_state() + error_msg = psolver%get_error() + print *, "State after error attempt: ", state + print *, "Expected: ", SOLVER_ERROR + print *, "Match? ", state == SOLVER_ERROR + print *, "Error message: '", trim(error_msg), "'" + print *, "" + + ! 最终信息 + print *, "=== Physics Solver Test Complete ===" + print *, "✓ Physics solver creation works" + print *, "✓ Physics component initialization works" + print *, "✓ Physics-enabled time stepping works" + print *, "✓ Physics disabled mode works" + print *, "✓ Different physics configurations work" + print *, "✓ Cleanup and error handling work" + print *, "" + print *, "下一步: 实现完整的数值方法(重构、通量、时间积分)" + +end program test_physics_solver \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_physics_solver_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_physics_solver_simple.f90 new file mode 100644 index 00000000..13312efd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_physics_solver_simple.f90 @@ -0,0 +1,161 @@ +! tests/test_physics_solver_simple.f90 +program test_physics_solver_simple + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction + use mesh_module, only: mesh_type + use physics_solver_module, only: physics_solver, SOLVER_COMPLETED + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(physics_solver) :: solver + real(wp) :: final_time, final_step + integer :: state + + print *, "=========================================" + print *, " 简单物理求解器测试" + print *, "=========================================" + print *, "" + + ! 步骤1: 配置 + print *, "[步骤1] 配置求解器..." + print *, "---------------------" + + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%dt = 0.01_wp + config%final_time = 0.1_wp + config%wave_speed = 1.0_wp + config%ic_type = "step" + config%boundary_type = "periodic" + config%equation_type = "linear_advection" + config%problem_type = "linear_advection" + config%enable_physics = .true. + config%domain_length = 1.0_wp + + print *, "配置参数:" + print *, " 重构格式: ", trim(config%recon_scheme) + print *, " 时间步长: ", config%dt + print *, " 最终时间: ", config%final_time + print *, " 波速: ", config%wave_speed + print *, "" + + ! 步骤2: 创建网格 + print *, "[步骤2] 创建网格..." + print *, "-------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + + print *, "网格信息:" + print *, " 单元数: ", mesh%ncells + print *, " 节点数: ", mesh%nnodes + print *, " 网格间距: ", mesh%dx + print *, "" + + ! 步骤3: 创建求解器 + print *, "[步骤3] 创建求解器..." + print *, "---------------------" + + solver = physics_solver(config, mesh) + + print *, "求解器创建成功" + print *, " 初始状态: ", solver%get_state() + print *, "" + + ! 步骤4: 初始化 + print *, "[步骤4] 初始化求解器..." + print *, "-----------------------" + + call solver%initialize() + + state = solver%get_state() + print *, "初始化完成" + print *, " 状态: ", state + print *, " 当前时间: ", solver%current_time + print *, " 当前步数: ", solver%current_step + print *, "" + + ! 步骤5: 运行求解器 + print *, "[步骤5] 运行求解器..." + print *, "---------------------" + + call solver%run_to_time(config%final_time) + + state = solver%get_state() + print *, "运行完成" + print *, " 状态: ", state + print *, " 最终时间: ", solver%current_time + print *, " 总步数: ", solver%current_step + print *, "" + + ! 步骤6: 保存结果 + print *, "[步骤6] 保存结果..." + print *, "-------------------" + + final_time = solver%current_time + final_step = real(solver%current_step, wp) + state = solver%get_state() + + print *, "保存的结果:" + print *, " 状态: ", state + print *, " 时间: ", final_time + print *, " 步数: ", final_step + print *, "" + + ! 步骤7: 清理求解器 + print *, "[步骤7] 清理求解器..." + print *, "---------------------" + + call solver%cleanup() + + print *, "清理后状态:" + print *, " 状态: ", solver%get_state() + print *, " 时间: ", solver%current_time + print *, " 步数: ", solver%current_step + print *, "" + + ! 步骤8: 验证结果 + print *, "[步骤8] 验证结果..." + print *, "-------------------" + + print *, "验证标准:" + print *, " 1. 运行后状态应为 COMPLETED (", SOLVER_COMPLETED, ")" + print *, " 2. 最终时间应接近 ", config%final_time + print *, " 3. 步数应大于 0" + print *, "" + + if (state == SOLVER_COMPLETED) then + print *, "✓ 状态验证通过: COMPLETED" + else + print *, "✗ 状态验证失败: 期望 ", SOLVER_COMPLETED, ", 实际 ", state + end if + + if (abs(final_time - config%final_time) < 1e-5_wp) then + print *, "✓ 时间验证通过: ", final_time, " ≈ ", config%final_time + else + print *, "✗ 时间验证失败: ", final_time, " ≠ ", config%final_time + end if + + if (final_step > 0) then + print *, "✓ 步数验证通过: ", final_step, " > 0" + else + print *, "✗ 步数验证失败: ", final_step, " ≤ 0" + end if + + print *, "" + + ! 最终判断 + if (state == SOLVER_COMPLETED .and. & + abs(final_time - config%final_time) < 1e-5_wp .and. & + final_step > 0) then + print *, "=========================================" + print *, " 所有测试通过! ✓" + print *, "=========================================" + else + print *, "=========================================" + print *, " 测试失败 ✗" + print *, "=========================================" + end if + +end program test_physics_solver_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_registry.f90 new file mode 100644 index 00000000..e82651ff --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_registry.f90 @@ -0,0 +1,87 @@ +! tests/test_registry.f90 (原test_minimal_simple.f90) +program test_registry + use base_modules, only: wp + use registry_module + use config_module + use mesh_module + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== 注册系统功能测试 ===" + print *, "" + + ! 测试1: 配置系统 + print *, "1. 测试配置系统" + print *, "--------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! 测试2: 网格系统 + print *, "2. 测试网格系统" + print *, "--------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! 测试3: 注册系统 + print *, "3. 测试注册系统" + print *, "--------------" + + call registry_init() + + ! 注册组件 + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call list_components() + print *, "注册表大小: ", registry_get_size() + print *, "" + + ! 测试组件查找 + print *, "4. 测试组件查找" + print *, "--------------" + + if (has_component_simple("reconstructor", "eno")) then + print *, "找到: reconstructor.eno" + else + print *, "未找到: reconstructor.eno" + end if + + if (has_component_simple("reconstructor", "unknown")) then + print *, "找到: reconstructor.unknown" + else + print *, "未找到: reconstructor.unknown" + end if + print *, "" + + ! 测试获取可用组件 + print *, "5. 测试注册系统功能" + print *, "------------------" + print *, "注册表已初始化: ", registry_is_initialized() + print *, "组件数量: ", registry_get_size() + print *, "" + + ! 清理 + call registry_cleanup() + + print *, "=== 注册系统测试完成 ===" + +end program test_registry \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_simple_link.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_simple_link.f90 new file mode 100644 index 00000000..71cc614e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_simple_link.f90 @@ -0,0 +1,78 @@ +! tests/test_simple_link.f90 +program test_simple_link + use base_modules, only: wp ! ← 添加这行 + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call registry_init() + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call list_components() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component_simple("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component_simple("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Cleanup + call registry_cleanup() + + print *, "=== Minimal test completed successfully ===" + +end program test_simple_link \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_solver_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_solver_base.f90 new file mode 100644 index 00000000..6cfe47e4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_solver_base.f90 @@ -0,0 +1,99 @@ +! tests/test_solver_base.f90 (修复版) +program test_solver_base + ! 所有 USE 语句必须在程序开始处 + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + use mesh_module, only: mesh_type + use solver_base_module, only: solver_base, SOLVER_UNINITIALIZED, & + SOLVER_INITIALIZED, SOLVER_COMPLETED, SOLVER_ERROR + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(solver_base) :: solver + integer :: state + + print *, "=== Solver Base Test ===" + print *, "" + + ! 测试1: 创建求解器 + print *, "1. Creating solver..." + print *, "----------------------" + + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%dt = 0.01_wp + + call config_print(config) + + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + solver = solver_base(config, mesh) + call solver%print_info() + print *, "" + + ! 测试2: 初始化 + print *, "2. Initializing solver..." + print *, "-------------------------" + + call solver%initialize() + state = solver%get_state() + print *, "State after initialization: ", state + print *, "Expected: ", SOLVER_INITIALIZED + print *, "Match? ", state == SOLVER_INITIALIZED + print *, "Error message: '", trim(solver%get_error()), "'" + print *, "" + + ! 测试3: 运行求解器 + print *, "3. Running solver..." + print *, "--------------------" + + call solver%run_to_time(0.05_wp) + state = solver%get_state() + print *, "State after run: ", state + print *, "Expected: ", SOLVER_COMPLETED + print *, "Match? ", state == SOLVER_COMPLETED + print *, "Current time: ", solver%current_time + print *, "Current step: ", solver%current_step + print *, "" + + ! 测试4: 再次运行(从已完成状态) + print *, "4. Running again from completed state..." + print *, "----------------------------------------" + + ! 需要先清理才能重新运行 + call solver%cleanup() + call solver%initialize() + call solver%run_to_time(0.1_wp) + + call solver%print_info() + print *, "" + + ! 测试5: 错误处理 + print *, "5. Testing error states..." + print *, "--------------------------" + + ! 创建一个未初始化的求解器 + call solver%cleanup() + state = solver%get_state() + print *, "Uninitialized state: ", state + print *, "Expected: ", SOLVER_UNINITIALIZED + print *, "Match? ", state == SOLVER_UNINITIALIZED + + ! 尝试运行未初始化的求解器 + call solver%run_to_time(0.01_wp) + state = solver%get_state() + print *, "State after error: ", state + print *, "Expected: ", SOLVER_ERROR + print *, "Match? ", state == SOLVER_ERROR + print *, "Error message: '", trim(solver%get_error()), "'" + print *, "" + + print *, "=== Solver Base Test Complete ===" + print *, "✓ Solver base class works" + print *, "✓ State management works" + print *, "✓ Time stepping framework works" + print *, "✓ Error handling works" + +end program test_solver_base \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_solver_framework.f90 b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_solver_framework.f90 new file mode 100644 index 00000000..6754323d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03e/tests/test_solver_framework.f90 @@ -0,0 +1,91 @@ +! tests/test_solver_framework.f90 +program test_solver_framework + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use mesh_module, only: mesh_type + use solver_module, only: cfd_solver, solver_create, solver_run, solver_cleanup + use solver_module, only: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, & + SOLVER_COMPLETED, SOLVER_ERROR + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(cfd_solver) :: solver + + print *, "=== 求解器框架测试 ===" + print *, "" + + ! 测试1: 基本创建 + print *, "1. 测试求解器创建..." + print *, "----------------------" + + ! 创建配置 + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.0_real64 + config%dt = 0.01_real64 + + call config_print(config) + print *, "" + + ! 创建网格 + call mesh%init(xmin=0.0_real64, xmax=2.0_real64, ncells=20) + call mesh%print_info() + print *, "" + + ! 创建求解器 + solver = solver_create(config, mesh) + print *, "✓ 求解器创建成功" + print *, " 状态: ", solver%get_state() + print *, "" + + ! 测试2: 求解器初始化 + print *, "2. 测试求解器初始化..." + print *, "------------------------" + + call solver%initialize() + print *, "✓ 求解器初始化完成" + print *, " 状态: ", solver%get_state() + print *, " 错误信息: '", trim(solver%get_error()), "'" + print *, "" + + ! 测试3: 简单运行 + print *, "3. 测试求解器运行..." + print *, "----------------------" + + call solver_run(solver, 0.05_real64) ! 运行到0.05秒 + print *, "✓ 求解器运行完成" + print *, " 最终状态: ", solver%get_state() + print *, "" + + ! 测试4: 清理 + print *, "4. 测试求解器清理..." + print *, "----------------------" + + call solver_cleanup(solver) + print *, "✓ 求解器清理完成" + print *, " 状态: ", solver%get_state() + print *, "" + + ! 测试5: 错误处理 + print *, "5. 测试错误处理..." + print *, "-------------------" + + ! 尝试重复初始化 + call solver%initialize() + print *, " 重复初始化状态: ", solver%get_state() + print *, " 错误信息: '", trim(solver%get_error()), "'" + + call solver_cleanup(solver) + print *, "" + + print *, "=== 框架测试总结 ===" + print *, "✓ 求解器创建/初始化/运行/清理流程验证完成" + print *, "✓ 状态管理正常工作" + print *, "✓ 错误处理机制就绪" + print *, "" + print *, "下一步: 添加实际数值计算功能" + +end program test_solver_framework \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03f/CMakeLists.txt new file mode 100644 index 00000000..55859dc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) +add_subdirectory(examples) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/README.md b/example/1d-linear-convection/weno3/fortran/registry/03f/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/examples/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03f/examples/CMakeLists.txt new file mode 100644 index 00000000..cff65214 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/examples/CMakeLists.txt @@ -0,0 +1,39 @@ +# examples/CMakeLists.txt (修正版) +message(STATUS "配置示例程序...") + +# 主示例程序:ENO/WENO对比(当前版本) +add_executable(run_eno_weno + run_eno_weno.f90 +) + +target_link_libraries(run_eno_weno + PRIVATE + solver + infrastructure + core + physics + manager + results + boundary + initial_condition +) + +# 集成版本示例 +add_executable(run_eno_weno_integrated + run_eno_weno_integrated.f90 +) + +target_link_libraries(run_eno_weno_integrated + PRIVATE + solver_integrated # 主要模块 + infrastructure + base + boundary # 集成求解器需要的边界条件 + initial_condition # 集成求解器需要的初始条件 +) + +install(TARGETS run_eno_weno run_eno_weno_integrated + RUNTIME DESTINATION bin/examples +) + +message(STATUS "示例程序配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/examples/run_eno_weno.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/examples/run_eno_weno.f90 new file mode 100644 index 00000000..ffa6ff9d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/examples/run_eno_weno.f90 @@ -0,0 +1,327 @@ +! examples/run_eno_weno.f90 (修正版) +program run_eno_weno + ! Example program: ENO/WENO comparison analysis + + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction, config_print + use mesh_module, only: mesh_type + use registry_module, only: registry_init, registry_cleanup, initialize_default_components + use physics_solver_module, only: physics_solver, SOLVER_COMPLETED + use results_module, only: results_saver, save_results ! 修改:只导入需要的内容 + + implicit none + + type(cfd_config) :: config_eno3, config_weno3, config_weno5 + type(mesh_type) :: mesh + type(physics_solver) :: solver_eno3, solver_weno3, solver_weno5 + + ! 结果保存器 + type(results_saver) :: saver ! 直接声明类型 + + ! Variables to save results + real(wp) :: time_eno3, time_weno3, time_weno5 + integer :: steps_eno3, steps_weno3, steps_weno5 + integer :: state_eno3, state_weno3, state_weno5 + logical :: all_success + + ! Debug: print start marker + print *, "==========================================" + print *, "START: ENO/WENO Comparison Analysis" + print *, "==========================================" + print *, "" + + ! Step 0: Initialize system + print *, "[STEP 0] Initializing system..." + print *, "--------------------------------" + + call registry_init(verbose=.true.) + print *, "Registry initialized" + + call initialize_default_components() + print *, "Default components registered" + print *, "" + + ! Step 1: Create mesh + print *, "[STEP 1] Creating computational mesh..." + print *, "----------------------------------------" + + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=40) + call mesh%print_info() + print *, "" + + ! 创建结果保存器 - 使用构造函数而不是赋值 + saver = results_saver("results", .true.) ! 直接使用构造函数 + + ! ========== ENO3 Solver ========== + print *, "[STEP 2] Configuring and running ENO3 solver..." + print *, "-----------------------------------------------" + + ! Configure ENO3 + config_eno3%verbose = .true. + config_eno3%ic_type = "step" + config_eno3%wave_speed = 1.0_wp + config_eno3%final_time = 0.625_wp + config_eno3%dt = 0.0025_wp + config_eno3%rk_order = 2 + config_eno3%boundary_type = "periodic" + config_eno3%equation_type = "linear_advection" + config_eno3%problem_type = "linear_advection" + config_eno3%enable_physics = .true. + config_eno3%domain_length = 2.0_wp + + call config_with_reconstruction(config_eno3, "eno", 3) + + print *, "ENO3 configuration:" + call config_print(config_eno3) + print *, "" + + ! Create and run ENO3 solver + print *, "Creating ENO3 solver instance..." + ! 替换这三行: + ! call physics_solver_constructor(solver_eno3, config_eno3, mesh) + + ! 改为: + solver_eno3 = physics_solver(config_eno3, mesh) + + print *, "Initializing ENO3 solver..." + call solver_eno3%initialize() + + print *, "Running ENO3 solver..." + call solver_eno3%run_to_time(config_eno3%final_time) + + ! Immediately save ENO3 results + time_eno3 = solver_eno3%current_time + steps_eno3 = solver_eno3%current_step + state_eno3 = solver_eno3%get_state() + + print *, "ENO3 solver completed" + print *, " Final time: ", time_eno3 + print *, " Total steps: ", steps_eno3 + print *, " State: ", state_eno3 + print *, "" + + ! 保存ENO3结果到文件 + call save_results(saver, "ENO3", & + solver_eno3%config, solver_eno3%mesh, solver_eno3%domain, & + solver_eno3%solution, & + solver_eno3%current_time, solver_eno3%current_step, solver_eno3%get_state()) + + ! ========== WENO3 Solver ========== + print *, "[STEP 3] Configuring and running WENO3 solver..." + print *, "------------------------------------------------" + + ! Configure WENO3 + config_weno3%verbose = .true. + config_weno3%ic_type = "step" + config_weno3%wave_speed = 1.0_wp + config_weno3%final_time = 0.625_wp + config_weno3%dt = 0.0025_wp + config_weno3%rk_order = 2 + config_weno3%boundary_type = "periodic" + config_weno3%equation_type = "linear_advection" + config_weno3%problem_type = "linear_advection" + config_weno3%enable_physics = .true. + config_weno3%domain_length = 2.0_wp + + call config_with_reconstruction(config_weno3, "weno3", 3) + + print *, "WENO3 configuration:" + call config_print(config_weno3) + print *, "" + + ! Create and run WENO3 solver + print *, "Creating WENO3 solver instance..." + call physics_solver_constructor(solver_weno3, config_weno3, mesh) + + print *, "Initializing WENO3 solver..." + call solver_weno3%initialize() + + print *, "Running WENO3 solver..." + call solver_weno3%run_to_time(config_weno3%final_time) + + ! Immediately save WENO3 results + time_weno3 = solver_weno3%current_time + steps_weno3 = solver_weno3%current_step + state_weno3 = solver_weno3%get_state() + + print *, "WENO3 solver completed" + print *, " Final time: ", time_weno3 + print *, " Total steps: ", steps_weno3 + print *, " State: ", state_weno3 + print *, "" + + ! 保存WENO3结果到文件 + call save_results(saver, "WENO3", & + solver_weno3%config, solver_weno3%mesh, solver_weno3%domain, & + solver_weno3%solution, time_weno3, steps_weno3, state_weno3) + + ! ========== WENO5 Solver ========== + print *, "[STEP 4] Configuring and running WENO5 solver..." + print *, "------------------------------------------------" + + ! Configure WENO5 + config_weno5%verbose = .true. + config_weno5%ic_type = "step" + config_weno5%wave_speed = 1.0_wp + config_weno5%final_time = 0.625_wp + config_weno5%dt = 0.0025_wp + config_weno5%rk_order = 2 + config_weno5%boundary_type = "periodic" + config_weno5%equation_type = "linear_advection" + config_weno5%problem_type = "linear_advection" + config_weno5%enable_physics = .true. + config_weno5%domain_length = 2.0_wp + + call config_with_reconstruction(config_weno5, "weno", 5) + + print *, "WENO5 configuration:" + call config_print(config_weno5) + print *, "" + + ! Create and run WENO5 solver + print *, "Creating WENO5 solver instance..." + call physics_solver_constructor(solver_weno5, config_weno5, mesh) + + print *, "Initializing WENO5 solver..." + call solver_weno5%initialize() + + print *, "Running WENO5 solver..." + call solver_weno5%run_to_time(config_weno5%final_time) + + ! Immediately save WENO5 results + time_weno5 = solver_weno5%current_time + steps_weno5 = solver_weno5%current_step + state_weno5 = solver_weno5%get_state() + + print *, "WENO5 solver completed" + print *, " Final time: ", time_weno5 + print *, " Total steps: ", steps_weno5 + print *, " State: ", state_weno5 + print *, "" + + ! 保存WENO5结果到文件 + call save_results(saver, "WENO5", & + solver_weno5%config, solver_weno5%mesh, solver_weno5%domain, & + solver_weno5%solution, time_weno5, steps_weno5, state_weno5) + + ! ========== Results Summary ========== + print *, "==========================================" + print *, " RESULTS SUMMARY" + print *, "==========================================" + print *, "" + + print *, "Solver Performance Comparison:" + print *, "------------------------------" + + print *, "ENO3:" + print *, " Final time: ", time_eno3 + print *, " Total steps: ", steps_eno3 + print *, " State: ", state_eno3 + print *, " Results saved to: results_ENO3_40.dat" + print *, "" + + print *, "WENO3:" + print *, " Final time: ", time_weno3 + print *, " Total steps: ", steps_weno3 + print *, " State: ", state_weno3 + print *, " Results saved to: results_WENO3_40.dat" + print *, "" + + print *, "WENO5:" + print *, " Final time: ", time_weno5 + print *, " Total steps: ", steps_weno5 + print *, " State: ", state_weno5 + print *, " Results saved to: results_WENO5_40.dat" + print *, "" + + ! ========== Final Judgment ========== + print *, "==========================================" + print *, " FINAL JUDGMENT" + print *, "==========================================" + print *, "" + + all_success = (state_eno3 == SOLVER_COMPLETED) .and. & + (state_weno3 == SOLVER_COMPLETED) .and. & + (state_weno5 == SOLVER_COMPLETED) + + if (all_success) then + print *, "✓ ALL SOLVERS SUCCESSFULLY COMPLETED!" + print *, "" + print *, "Parameter Summary:" + print *, " Grid cells: ", mesh%ncells + print *, " Time step: ", config_eno3%dt + print *, " Final time: ", config_eno3%final_time + print *, " Wave speed: ", config_eno3%wave_speed + print *, " IC type: ", trim(config_eno3%ic_type) + print *, "" + print *, "Performance Comparison:" + print *, " ENO3: ", steps_eno3, " steps" + print *, " WENO3: ", steps_weno3, " steps" + print *, " WENO5: ", steps_weno5, " steps" + print *, "" + print *, "To visualize results:" + print *, " python ../python/plot_results.py --auto" + else + print *, "✗ SOME SOLVERS FAILED" + print *, "" + print *, "Failure Analysis:" + if (state_eno3 /= SOLVER_COMPLETED) then + print *, " • ENO3 failed with state: ", state_eno3 + end if + if (state_weno3 /= SOLVER_COMPLETED) then + print *, " • WENO3 failed with state: ", state_weno3 + end if + if (state_weno5 /= SOLVER_COMPLETED) then + print *, " • WENO5 failed with state: ", state_weno5 + end if + end if + + print *, "" + print *, "==========================================" + print *, " ANALYSIS COMPLETE" + print *, "==========================================" + + ! ========== Cleanup ========== + print *, "" + print *, "[STEP 5] Cleaning up system..." + print *, "--------------------------------" + + call solver_eno3%cleanup() + call solver_weno3%cleanup() + call solver_weno5%cleanup() + call registry_cleanup() + + print *, "All solvers cleaned up" + print *, "Registry cleaned up" + print *, "" + + ! ========== Wait for user input before exit ========== + print *, "==========================================" + print *, "Press ENTER to exit..." + print *, "==========================================" + + ! Wait for user input (uncomment if needed) + ! read(*,*) + + ! Alternative: add a small delay + print *, "Program will exit in 3 seconds..." + call sleep(3) + + print *, "" + print *, "==========================================" + print *, " PROGRAM END" + print *, "==========================================" + +contains + + ! 辅助函数:显式创建physics_solver(如果原始代码使用函数而不是子程序) + subroutine physics_solver_constructor(solver, config, mesh) + type(physics_solver), intent(out) :: solver + type(cfd_config), intent(in) :: config + type(mesh_type), intent(in) :: mesh + + ! 使用赋值构造函数(如果physics_solver_module中有相应的接口) + solver = physics_solver(config, mesh) + end subroutine physics_solver_constructor + +end program run_eno_weno \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/examples/run_eno_weno_integrated.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/examples/run_eno_weno_integrated.f90 new file mode 100644 index 00000000..77629305 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/examples/run_eno_weno_integrated.f90 @@ -0,0 +1,176 @@ +! examples/run_eno_weno_integrated.f90 (完整修复版) +program run_eno_weno_integrated + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction, config_print + use mesh_module, only: mesh_type + use solver_integrated_module, only: integrated_solver, SOLVER_INITIALIZED, SOLVER_COMPLETED + + implicit none + + type(cfd_config) :: config_eno3, config_weno3, config_weno5 + type(mesh_type) :: mesh + type(integrated_solver) :: solver_eno3, solver_weno3, solver_weno5 + + ! 结果变量 + real(wp) :: time_eno3, time_weno3, time_weno5 + integer :: steps_eno3, steps_weno3, steps_weno5 + integer :: state_eno3, state_weno3, state_weno5 + logical :: all_success + + ! 定义常量(避免导入冲突) + integer, parameter :: SOLVER_READY = 0 + integer, parameter :: SOLVER_RUNNING = 2 + integer, parameter :: SOLVER_ERROR = -1 + + print *, "==========================================" + print *, "ENO/WENO 对比分析 (集成版本)" + print *, "==========================================" + print *, "" + + ! 步骤1: 创建网格 + print *, "[STEP 1] 创建计算网格..." + print *, "-----------------------------------" + + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=40) + call mesh%print_info() + print *, "" + + ! ========== ENO3 求解器 ========== + print *, "[STEP 2] 配置和运行 ENO3 求解器..." + print *, "-----------------------------------" + + ! 初始化配置 + config_eno3%verbose = .true. + config_eno3%ic_type = "step" + config_eno3%wave_speed = 1.0_wp + config_eno3%final_time = 0.625_wp + config_eno3%dt = 0.0025_wp + config_eno3%boundary_type = "periodic" + config_eno3%equation_type = "linear_advection" + config_eno3%problem_type = "linear_advection" + config_eno3%domain_length = 2.0_wp + config_eno3%enable_physics = .true. + + call config_with_reconstruction(config_eno3, "eno", 3) + call config_print(config_eno3) + print *, "" + + ! 创建和运行求解器 + solver_eno3%config = config_eno3 + solver_eno3%mesh = mesh + + ! 控制数据模式 (当前使用简单数据) + call solver_eno3%enable_real_data(.false.) ! 设置为false使用简单数据 + + call solver_eno3%initialize() + call solver_eno3%run_to_time(config_eno3%final_time) + + ! 获取结果 + time_eno3 = solver_eno3%current_time + steps_eno3 = solver_eno3%current_step + state_eno3 = solver_eno3%get_state() + + print *, "ENO3 完成:" + print *, " 最终时间: ", time_eno3 + print *, " 总步数: ", steps_eno3 + print *, " 状态: ", state_eno3 + print *, "" + + ! ========== WENO3 求解器 ========== + print *, "[STEP 3] 配置和运行 WENO3 求解器..." + print *, "-----------------------------------" + + ! 配置 (复制ENO3配置,只改重构格式) + config_weno3 = config_eno3 + call config_with_reconstruction(config_weno3, "weno3", 3) + + ! 运行 + solver_weno3%config = config_weno3 + solver_weno3%mesh = mesh + call solver_weno3%enable_real_data(.false.) + + call solver_weno3%initialize() + call solver_weno3%run_to_time(config_weno3%final_time) + + time_weno3 = solver_weno3%current_time + steps_weno3 = solver_weno3%current_step + state_weno3 = solver_weno3%get_state() + + print *, "WENO3 完成:" + print *, " 最终时间: ", time_weno3 + print *, " 总步数: ", steps_weno3 + print *, " 状态: ", state_weno3 + print *, "" + + ! ========== WENO5 求解器 ========== + print *, "[STEP 4] 配置和运行 WENO5 求解器..." + print *, "-----------------------------------" + + config_weno5 = config_eno3 + call config_with_reconstruction(config_weno5, "weno", 5) + + solver_weno5%config = config_weno5 + solver_weno5%mesh = mesh + call solver_weno5%enable_real_data(.false.) + + call solver_weno5%initialize() + call solver_weno5%run_to_time(config_weno5%final_time) + + time_weno5 = solver_weno5%current_time + steps_weno5 = solver_weno5%current_step + state_weno5 = solver_weno5%get_state() + + print *, "WENO5 完成:" + print *, " 最终时间: ", time_weno5 + print *, " 总步数: ", steps_weno5 + print *, " 状态: ", state_weno5 + print *, "" + + ! ========== 结果汇总 ========== + print *, "==========================================" + print *, " 结果汇总" + print *, "==========================================" + print *, "" + + all_success = (state_eno3 == SOLVER_COMPLETED) .and. & + (state_weno3 == SOLVER_COMPLETED) .and. & + (state_weno5 == SOLVER_COMPLETED) + + if (all_success) then + print *, "✓ 所有求解器成功完成!" + print *, "" + print *, "性能对比:" + print *, " ENO3: ", steps_eno3, " 步" + print *, " WENO3: ", steps_weno3, " 步" + print *, " WENO5: ", steps_weno5, " 步" + print *, "" + print *, "网格信息:" + print *, " 单元数: ", mesh%ncells + print *, " 域长度: ", mesh%L + print *, " 网格间距: ", mesh%dx + print *, "" + print *, "数据模式: 简单数据 (一阶迎风格式)" + print *, "切换真实计算: 调用 solver%enable_real_data(.true.)" + else + print *, "✗ 部分求解器失败" + print *, " ENO3状态: ", state_eno3, " (期望: ", SOLVER_COMPLETED, ")" + print *, " WENO3状态: ", state_weno3, " (期望: ", SOLVER_COMPLETED, ")" + print *, " WENO5状态: ", state_weno5, " (期望: ", SOLVER_COMPLETED, ")" + end if + + print *, "" + print *, "==========================================" + print *, " 分析完成" + print *, "==========================================" + + ! 清理 + call solver_eno3%cleanup() + call solver_weno3%cleanup() + call solver_weno5%cleanup() + + ! 等待用户输入 + print *, "" + print *, "按 ENTER 键退出..." + read(*,*) + +end program run_eno_weno_integrated \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/python/plot_results.py b/example/1d-linear-convection/weno3/fortran/registry/03f/python/plot_results.py new file mode 100644 index 00000000..cf8e4017 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/python/plot_results.py @@ -0,0 +1,419 @@ +#!/usr/bin/env python3 +# python/plot_results.py +""" +Fortran CFD 结果可视化脚本 +与Julia的plotter.jl功能类似,但直接读取Fortran生成的文本文件 +""" + +import numpy as np +import matplotlib.pyplot as plt +import os +import sys +import glob +from pathlib import Path +import re + +class FortranResultsPlotter: + """Fortran结果绘图器""" + + def __init__(self, style='default'): + self.style = style + self.setup_styles() + + def setup_styles(self): + """设置绘图样式""" + if self.style == 'default': + self.styles = { + 'numerical': { + 'color': 'blue', + 'linestyle': '-', + 'marker': 'o', + 'markerfacecolor': 'none', + 'markersize': 4, + 'linewidth': 1 + }, + 'analytical': { + 'color': 'red', + 'linestyle': '--', + 'marker': '', + 'linewidth': 1.5 + }, + 'comparison': [ + {'color': 'black', 'linestyle': '-', 'marker': 'o', 'markerfacecolor': 'none'}, + {'color': 'blue', 'linestyle': '--', 'marker': 's', 'markerfacecolor': 'none'}, + {'color': 'green', 'linestyle': ':', 'marker': '^', 'markerfacecolor': 'none'} + ] + } + + def read_fortran_results(self, filename): + """读取Fortran生成的结果文件""" + data = {} + + try: + with open(filename, 'r', encoding='utf-8', errors='ignore') as f: + lines = f.readlines() + + data_lines = [] + in_data_section = False # 新状态:是否在数据区域 + + for line in lines: + line = line.strip() + if not line: + continue + + # 跳过所有分隔线 + if line.startswith("===="): + continue + + # 检测数据区域开始 + if line == "DATA: x, numerical, analytical": + in_data_section = True + continue + + if not in_data_section: + # 解析头部信息 + if "Solver:" in line: + data['solver'] = line.split(":", 1)[1].strip() + elif "Scheme:" in line: + data['scheme'] = line.split(":", 1)[1].strip() + elif "Order:" in line: + if "RK Order:" in line: + data['rk_order'] = int(line.split(":", 1)[1].strip()) + else: + data['order'] = int(line.split(":", 1)[1].strip()) + elif "Current Time:" in line: + data['time'] = float(line.split(":", 1)[1].strip()) + elif "Grid Points:" in line: + data['n_points'] = int(line.split(":", 1)[1].strip()) + else: + # 解析数据行 + parts = line.split() + if len(parts) >= 3: + try: + x = float(parts[0]) + numerical = float(parts[1]) + analytical = float(parts[2]) + data_lines.append([x, numerical, analytical]) + except ValueError: + continue # 忽略无法解析的行 + + if data_lines: + import numpy as np + data_array = np.array(data_lines) + data['x'] = data_array[:, 0] + data['numerical'] = data_array[:, 1] + data['analytical'] = data_array[:, 2] + + print(f"Read {len(data['x'])} points from {filename}") + print(f" Solver: {data.get('solver', 'N/A')}") + print(f" Scheme: {data.get('scheme', 'N/A')} order {data.get('order', 'N/A')}") + print(f" Time: {data.get('time', 'N/A')}") + + return data + else: + print(f"Warning: No data found in {filename}") + return None + + except Exception as e: + print(f"Error reading {filename}: {e}") + return None + + def plot_single_result(self, filename, title=None, show=True, save_path=None): + """绘制单个结果文件""" + data = self.read_fortran_results(filename) + if data is None: + return False + + fig, ax = plt.subplots(figsize=(10, 6)) + + # 自动生成标题 + if title is None: + title = f"1D Convection (t={data['time']:.3f})\n" + title += f"{data['order']}th-order {data['scheme'].upper()} + {data['rk_order']}nd-order RK" + + # 绘制数值解 + ax.plot(data['x'], data['numerical'], + label=f"Numerical ({data['scheme'].upper()}{data['order']})", + **self.styles['numerical']) + + # 绘制解析解 + ax.plot(data['x'], data['analytical'], + label="Analytical", + **self.styles['analytical']) + + # 设置图形属性 + ax.set_title(title, fontsize=12) + ax.set_xlabel("x", fontsize=10) + ax.set_ylabel("u", fontsize=10) + ax.legend(fontsize=9) + ax.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + + # 保存或显示 + if save_path: + plt.savefig(save_path, dpi=150, bbox_inches='tight') + print(f"Saved plot to {save_path}") + + if show: + plt.show() + else: + plt.close() + + return True + + def format_scheme_label(self, scheme: str, order: int) -> str: + s = scheme.upper() + if s.startswith("WENO"): + return f"WENO{order}" + else: + return f"{s}{order}" + + def plot_comparison(self, filenames, labels=None, title=None, show=True, save_path=None): + """比较多个结果文件""" + all_data = [] + print(f"plot_comparison filenames={filenames}") + + # 读取所有文件 + for filename in filenames: + print(f"plot_comparison filename={filename}") + data = self.read_fortran_results(filename) + if data is not None: + all_data.append(data) + + if not all_data: + print("No valid data to plot") + return False + + fig, axes = plt.subplots(2, 2, figsize=(14, 10)) + + # 子图1:所有方法比较 + for i, data in enumerate(all_data): + #print(f"i,all_data={i,all_data}") + if labels and i < len(labels): + label = labels[i] + else: + label = f"{data['scheme'].upper()}{data['order']}" + + style_idx = i % len(self.styles['comparison']) + style = self.styles['comparison'][style_idx] + + axes[0, 0].plot(data['x'], data['numerical'], + label=label, **style) + + axes[0, 0].plot(all_data[0]['x'], all_data[0]['analytical'], + label="Analytical", **self.styles['analytical']) + + axes[0, 0].set_xlabel('x', fontsize=12) + axes[0, 0].set_ylabel('u(x)', fontsize=12) + axes[0, 0].set_title('Numerical Solutions Comparison', fontsize=14) + axes[0, 0].legend() + axes[0, 0].grid(True, alpha=0.3) + + # 子图2:误差分析 + for i, data in enumerate(all_data): + if labels and i < len(labels): + label = labels[i] + else: + label = f"{data['scheme'].upper()}{data['order']}" + + error = np.abs(data['numerical'] - data['analytical']) + axes[0, 1].semilogy(data['x'], error, label=label) + + axes[0, 1].set_xlabel('x', fontsize=12) + axes[0, 1].set_ylabel('Absolute Error', fontsize=12) + axes[0, 1].set_title('Error Comparison', fontsize=14) + axes[0, 1].legend() + axes[0, 1].grid(True, alpha=0.3) + + # 子图3:数值解细节(放大) + x_min, x_max = all_data[0]['x'].min(), all_data[0]['x'].max() + zoom_center = 1.0 # 阶跃函数位置附近 + zoom_width = 0.3 + + for i, data in enumerate(all_data): + if labels and i < len(labels): + label = labels[i] + else: + label = f"{data['scheme'].upper()}{data['order']}" + + style_idx = i % len(self.styles['comparison']) + style = self.styles['comparison'][style_idx] + + axes[1, 0].plot(data['x'], data['numerical'], label=label, **style) + + axes[1, 0].plot(all_data[0]['x'], all_data[0]['analytical'], + label="Analytical", **self.styles['analytical']) + + axes[1, 0].set_xlim(zoom_center - zoom_width/2, zoom_center + zoom_width/2) + axes[1, 0].set_xlabel('x', fontsize=12) + axes[1, 0].set_ylabel('u(x)', fontsize=12) + axes[1, 0].set_title('Zoomed View (x ≈ 1.0)', fontsize=14) + axes[1, 0].legend() + axes[1, 0].grid(True, alpha=0.3) + + # 子图4:性能统计 + axes[1, 1].axis('off') + + # 计算并显示L2误差 + errors = [] + schemes = [] + + for data in all_data: + error = np.sqrt(np.mean((data['numerical'] - data['analytical'])**2)) + errors.append(error) + schemes.append(f"{data['scheme'].upper()}{data['order']}") + + # 创建表格数据 + table_data = [] + for i, (scheme, error) in enumerate(zip(schemes, errors)): + table_data.append([scheme, f"{error:.2e}"]) + + # 在子图中显示表格 + table = axes[1, 1].table(cellText=table_data, + colLabels=['Scheme', 'L2 Error'], + loc='center', + cellLoc='center', + colWidths=[0.3, 0.4]) + + table.auto_set_font_size(False) + table.set_fontsize(10) + table.scale(1, 1.5) + + axes[1, 1].set_title('Performance Summary (L2 Error)', fontsize=14) + + # 设置总标题 + if title is None: + time = all_data[0]['time'] + schemes_str = ", ".join([self.format_scheme_label(d['scheme'], d['order']) for d in all_data]) + title = f"1D Convection Comparison (t={time:.3f})\n{schemes_str}" + + fig.suptitle(title, fontsize=16) + plt.tight_layout() + + # 保存或显示 + if save_path: + plt.savefig(save_path, dpi=150, bbox_inches='tight') + print(f"Saved comparison plot to {save_path}") + + if show: + plt.show() + else: + plt.close() + + return True + + def get_scheme_from_filename(self, filename): + print(f"filename={filename}") + stem = Path(filename).stem # e.g., "results_ENO3_40" + parts = stem.split('_') + if len(parts) >= 2: + return parts[1] # "ENO3", "WENO3", "WENO5" + return "" + + def plot_eno_weno_comparison(self, result_dir=".", save_path="eno_weno_comparison.png"): + """自动绘制ENO/WENO对比图(类似Julia的功能)""" + # 查找结果文件 + pattern = os.path.join(result_dir, "results_*.dat") + files = glob.glob(pattern) + + if not files: + print(f"No result files found matching {pattern}") + return False + + # 重新分类 + file_info = [] + eno_files = [] + weno3_files = [] + weno5_files = [] + for f in files: + print(f"f={f}") + print(f"type(f)={type(f)}") + scheme = self.get_scheme_from_filename(f) + print(f"scheme={scheme}") + if scheme == "ENO3": + eno_files.append(f) + elif scheme == "WENO3": + weno3_files.append(f) + elif scheme == "WENO5": + weno5_files.append(f) + + # 按求解器类型分类 + print(f"eno_files={eno_files}") + print(f"weno3_files={weno3_files}") + print(f"weno5_files={weno5_files}") + + # 选择最新的文件(如果有多组) + files_to_plot = [] + labels = [] + + if eno_files: + files_to_plot.append(sorted(eno_files)[-1]) + labels.append("ENO3") + + if weno3_files: + files_to_plot.append(sorted(weno3_files)[-1]) + labels.append("WENO3") + + if weno5_files: + files_to_plot.append(sorted(weno5_files)[-1]) + labels.append("WENO5") + + if len(files_to_plot) >= 2: + print(f"Plotting comparison of {len(files_to_plot)} solvers") + return self.plot_comparison(files_to_plot, labels=labels, + save_path=save_path, show=True) + else: + print("Not enough different solvers for comparison") + return False + +def main(): + """主函数""" + import argparse + + parser = argparse.ArgumentParser(description='Fortran CFD Results Visualizer') + parser.add_argument('--file', help='Plot single result file') + parser.add_argument('--compare', nargs='+', help='Compare multiple files') + parser.add_argument('--dir', default='.', help='Directory containing result files') + parser.add_argument('--auto', action='store_true', help='Auto plot ENO/WENO comparison') + parser.add_argument('--save', help='Save plot to file (without showing)') + parser.add_argument('--no-show', action='store_true', help='Don\'t show plot') + + args = parser.parse_args() + + plotter = FortranResultsPlotter() + + if args.file: + # 绘制单个文件 + plotter.plot_single_result(args.file, save_path=args.save, + show=not args.no_show) + + elif args.compare: + # 比较多个文件 + plotter.plot_comparison(args.compare, save_path=args.save, + show=not args.no_show) + + elif args.auto: + # 自动绘制比较图 + save_path = args.save or "eno_weno_comparison.png" + plotter.plot_eno_weno_comparison(args.dir, save_path=save_path) + + else: + # 默认:显示帮助 + parser.print_help() + + # 也显示可用的结果文件 + print("\nAvailable result files:") + pattern = os.path.join(args.dir, "results_*.dat") + files = glob.glob(pattern) + + if files: + for f in sorted(files): + print(f" {os.path.basename(f)}") + + print(f"\nTo plot ENO/WENO comparison automatically:") + print(f" python plot_results.py --auto") + else: + print(" No result files found") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/build.bat new file mode 100644 index 00000000..6fd6dc03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/build.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Project Builder +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python build script with full Intel environment support... +echo. + +python build.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Build failed + pause + exit /b 1 +) + +echo. +echo [INFO] Build completed successfully! +echo. +echo [INFO] To run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/build.py new file mode 100644 index 00000000..3bf6d537 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/build.py @@ -0,0 +1,629 @@ +#!/usr/bin/env python3 +""" +Fortran CFD Project Builder - 完整Python解决方案 +在Python内部处理Intel oneAPI环境配置 +""" + +import os +import sys +import subprocess +import shutil +import argparse +import time +import platform +import tempfile +from pathlib import Path + +class IntelEnvironment: + """Intel oneAPI环境管理器""" + + def __init__(self): + self.setvars_path = None + self.env_vars = {} + + def find_setvars(self): + """查找setvars.bat文件""" + possible_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + r"C:\Program Files (x86)\Intel\oneAPI\compiler\latest\env\vars.bat", + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\setvars.bat"), + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\compiler\latest\env\vars.bat"), + ] + + for path in possible_paths: + if os.path.exists(path): + self.setvars_path = path + return True + + return False + + def setup_environment(self): + """设置Intel环境""" + if not self.find_setvars(): + return False + + try: + # 创建临时的批处理文件来捕获环境变量 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + f.write(f'@echo off\n') + f.write(f'call "{self.setvars_path}" >nul 2>&1\n') + f.write(f'set\n') # 输出所有环境变量 + temp_bat = f.name + + # 运行批处理文件并捕获输出 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + shell=True + ) + + # 解析环境变量 + for line in result.stdout.split('\n'): + line = line.strip() + if '=' in line: + key, value = line.split('=', 1) + self.env_vars[key.strip()] = value.strip() + + # 清理临时文件 + os.unlink(temp_bat) + + # 更新当前进程的环境变量 + os.environ.update(self.env_vars) + + return True + + except Exception as e: + print(f"设置Intel环境失败: {e}") + return False + + def get_compiler_info(self): + """获取编译器信息""" + info = {} + + # 检查ifx编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + env={**os.environ, **self.env_vars} if self.env_vars else os.environ + ) + + if result.returncode == 0: + for line in result.stdout.split('\n'): + if 'Version' in line or '版本' in line: + info['ifx_version'] = line.strip() + break + except: + pass + + # 检查环境变量 + info['ifx_root'] = self.env_vars.get('IFX_ROOT', '') + info['compiler_root'] = self.env_vars.get('ONEAPI_ROOT', '') + + return info + +class BuildSystem: + """构建系统主类""" + + def __init__(self): + self.project_root = Path(__file__).parent.parent + self.build_dir = self.project_root / "build" + self.intel_env = IntelEnvironment() + + # 设置控制台编码 + if sys.platform == "win32": + try: + import ctypes + # 设置控制台输出为UTF-8 + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + except: + pass + + def print_header(self, text): + """打印标题""" + print(f"\n{'='*70}") + print(f" {text}") + print(f"{'='*70}\n") + + def print_step(self, step, total, message): + """打印步骤""" + print(f"[{step}/{total}] {message}...") + + def print_success(self, message): + """打印成功""" + print(f"\033[92m✓ {message}\033[0m") + + def print_error(self, message): + """打印错误""" + print(f"\033[91m✗ {message}\033[0m") + + def print_warning(self, message): + """打印警告""" + print(f"\033[93m! {message}\033[0m") + + def print_info(self, message): + """打印信息""" + print(f"\033[94mℹ {message}\033[0m") + + def check_prerequisites(self): + """检查前提条件""" + self.print_step(1, 6, "检查前提条件") + + # 检查Python版本 + python_version = sys.version.split()[0] + self.print_info(f"Python版本: {python_version}") + + # 检查平台 + self.print_info(f"平台: {platform.system()} {platform.release()}") + self.print_info(f"处理器核心数: {os.cpu_count()}") + + # 检查CMake + try: + result = subprocess.run( + ["cmake", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + version_line = result.stdout.split('\n')[0] + self.print_success(f"CMake: {version_line}") + else: + self.print_error("CMake未找到") + return False + except FileNotFoundError: + self.print_error("CMake未安装") + return False + + return True + + def setup_intel_environment(self, args): + """设置Intel环境""" + self.print_step(2, 6, "配置Intel oneAPI环境") + + if not self.intel_env.find_setvars(): + self.print_warning("未找到Intel oneAPI setvars.bat") + self.print_info("将尝试使用系统环境中的编译器") + + # 检查是否能直接访问编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + self.print_success("Intel编译器在系统PATH中找到") + return True + else: + self.print_warning("Intel编译器未在PATH中找到") + except: + self.print_warning("无法访问Intel编译器") + + return True # 继续,让CMake自己找编译器 + + # 设置环境 + if self.intel_env.setup_environment(): + compiler_info = self.intel_env.get_compiler_info() + + if compiler_info.get('ifx_version'): + self.print_success(f"Intel Fortran编译器: {compiler_info['ifx_version']}") + elif compiler_info.get('ifx_root'): + self.print_success(f"Intel编译器路径: {compiler_info['ifx_root']}") + else: + self.print_success("Intel oneAPI环境配置完成") + + return True + else: + self.print_warning("Intel环境配置失败,将继续使用系统环境") + return True + + def clean_build_directory(self, args): + """清理构建目录""" + if args.clean and self.build_dir.exists(): + self.print_info("清理构建目录...") + try: + shutil.rmtree(self.build_dir) + self.print_success("构建目录已清理") + except Exception as e: + self.print_error(f"清理失败: {e}") + if not args.force: + return False + return True + + def run_command(self, cmd, cwd=None, check=True, env=None): + """运行命令""" + if isinstance(cmd, list): + cmd_str = ' '.join(str(c) for c in cmd if c) + else: + cmd_str = str(cmd) + + print(f" \033[96m$\033[0m {cmd_str}") + + try: + # 合并环境变量 + exec_env = os.environ.copy() + if env: + exec_env.update(env) + if self.intel_env.env_vars: + exec_env.update(self.intel_env.env_vars) + + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=False, + env=exec_env + ) + + # 处理输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or '完成' in line or '生成' in line: + print(f" \033[92m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + if check and result.returncode != 0: + self.print_error(f"命令执行失败,退出码: {result.returncode}") + return False + + return True + + except Exception as e: + self.print_error(f"命令执行异常: {e}") + return False + + def configure_cmake(self, args): + """配置CMake""" + self.print_step(3, 6, "配置CMake项目") + + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ] + + if args.compiler == "ifx": + cmake_cmd.extend(["-T", "fortran=ifx"]) + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + success = self.run_command(cmake_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("CMake配置完成") + else: + self.print_error("CMake配置失败") + + return success + + def build_project(self, args): + """构建项目""" + self.print_step(4, 6, "构建项目") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + success = self.run_command(build_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("项目构建完成") + else: + self.print_error("构建失败") + + return success + + def run_tests_with_environment(self, test_exe): + """运行单个测试,确保有Intel环境""" + try: + # 创建临时的批处理文件来运行测试 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'"{test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'"{test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + return result + + except Exception as e: + print(f"运行测试失败: {e}") + return None + + def run_tests(self, args): + """运行测试""" + self.print_step(5, 6, "运行测试") + + # 查找测试可执行文件 + test_dir = self.build_dir / "bin" / args.build_type + if not test_dir.exists(): + test_dir = self.build_dir / "bin" + if not test_dir.exists(): + test_dir = self.build_dir + + test_files = list(test_dir.glob("test_*.exe")) + + if not test_files: + self.print_warning("未找到测试程序") + return True + + all_passed = True + + for test_exe in sorted(test_files): + test_name = test_exe.stem + self.print_info(f"运行测试: {test_name}") + print(f" {'-'*50}") + + # 运行测试 + result = self.run_tests_with_environment(str(test_exe)) + + if result is None: + self.print_error(f" {test_name} 运行失败") + all_passed = False + continue + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + print(f" {line}") + + if result.returncode == 0: + self.print_success(f" {test_name} 通过") + else: + self.print_error(f" {test_name} 失败 (退出码: {result.returncode})") + all_passed = False + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + print() # 空行 + + return all_passed + + def create_test_runner(self, args): + """创建独立的测试运行器""" + self.print_step(6, 6, "创建测试运行器") + + runner_path = self.build_dir / "run_tests.bat" + + content = f'''@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Test Runner +echo ======================================== +echo. + +REM Setup Intel oneAPI environment +set "SETVARS_PATH={self.intel_env.setvars_path or ''}" +if exist "%SETVARS_PATH%" ( + call "%SETVARS_PATH%" >nul + echo [INFO] Intel environment configured +) else ( + echo [WARNING] Intel environment not found + echo [WARNING] Tests may fail without runtime libraries +) + +echo. + +REM Run all test executables +set "TEST_COUNT=0" +set "PASS_COUNT=0" + +for %%f in ("bin\\{args.build_type}\\test_*.exe") do ( + set /a TEST_COUNT+=1 + echo [TEST %%f] + echo {'-'*50} + + %%f + if errorlevel 1 ( + echo [FAILED] %%f + ) else ( + echo [PASSED] %%f + set /a PASS_COUNT+=1 + ) + echo. +) + +echo ======================================== +echo Tests: %PASS_COUNT%/%TEST_COUNT% passed +if %PASS_COUNT% equ %TEST_COUNT% ( + echo [SUCCESS] All tests passed! +) else ( + echo [FAILURE] Some tests failed +) +echo ======================================== + +pause +''' + + with open(runner_path, 'w', encoding='utf-8') as f: + f.write(content) + + self.print_success(f"测试运行器已创建: {runner_path}") + self.print_info(f"使用方法: cd build && run_tests.bat") + + return runner_path + + def generate_report(self, args, build_time, tests_passed): + """生成构建报告""" + self.print_header("构建完成") + + print(f"项目: {self.project_root.name}") + print(f"构建类型: {args.build_type}") + print(f"编译器: {args.compiler}") + print(f"并行作业: {args.jobs}") + print(f"总耗时: {build_time:.1f}秒") + print(f"测试结果: {'全部通过' if tests_passed else '有失败'}") + + # 显示生成的可执行文件 + bin_dir = self.build_dir / "bin" / args.build_type + if bin_dir.exists(): + print(f"\n生成的可执行文件:") + for exe in sorted(bin_dir.glob("*.exe")): + size_mb = exe.stat().st_size / (1024 * 1024) + print(f" • {exe.name} ({size_mb:.2f} MB)") + + # 显示测试运行器信息 + runner_path = self.build_dir / "run_tests.bat" + if runner_path.exists(): + print(f"\n独立测试运行器:") + print(f" • {runner_path.name}") + print(f" 在Intel oneAPI环境中运行所有测试") + + def run(self): + """运行构建系统""" + parser = argparse.ArgumentParser( + description="Fortran CFD项目构建工具", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认构建 + %(prog)s --clean # 清理后构建 + %(prog)s --build-type Release # Release构建 + %(prog)s --no-tests # 只构建,不运行测试 + %(prog)s -j8 --verbose # 8线程并行构建,详细输出 + """ + ) + + parser.add_argument("--build-type", choices=["Debug", "Release"], + default="Debug", help="构建类型") + parser.add_argument("--compiler", choices=["ifx", "ifort"], + default="ifx", help="Fortran编译器") + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-tests", action="store_true", + help="跳过测试") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + + args = parser.parse_args() + + # 开始构建 + start_time = time.time() + + self.print_header("Fortran CFD 项目构建系统") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 检查前提条件 + if not self.check_prerequisites(): + if not args.force: + return 1 + + # 2. 设置Intel环境 + if not self.setup_intel_environment(args): + if not args.force: + return 1 + + # 3. 清理目录 + if not self.clean_build_directory(args): + if not args.force: + return 1 + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 4. 配置CMake + if not self.configure_cmake(args): + if not args.force: + return 1 + + # 5. 构建项目 + if not self.build_project(args): + if not args.force: + return 1 + + # 6. 运行测试和创建测试运行器 + tests_passed = True + if not args.no_tests: + tests_passed = self.run_tests(args) + + # 创建测试运行器 + self.create_test_runner(args) # 传递 args 参数 + + # 7. 生成报告 + build_time = time.time() - start_time + self.generate_report(args, build_time, tests_passed) + + return 0 if tests_passed else 1 + + except KeyboardInterrupt: + self.print_error("\n构建被用户中断") + return 1 + except Exception as e: + self.print_error(f"构建过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + builder = BuildSystem() + return builder.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/run_all_steps.bat b/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/run_all_steps.bat new file mode 100644 index 00000000..d506149b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/run_all_steps.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo CFD Project: All Steps +echo ======================================== +echo. + +echo [INFO] Starting Step 1: Physics Modules Test... +call run_step1.bat + +if errorlevel 1 ( + echo [ERROR] Step 1 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Starting Step 2: Configuration Physics Update... +call run_step2.bat + +if errorlevel 1 ( + echo [ERROR] Step 2 failed + pause + exit /b 1 +) + +echo. +echo ======================================== +echo All Steps Completed Successfully! +echo ======================================== +echo. +echo [INFO] Next: Update component manager for physics support +echo [INFO] Run: run_step3.bat (to be created) +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/run_example.py b/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/run_example.py new file mode 100644 index 00000000..d7c19917 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/run_example.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +# scripts/run_example.py +""" +运行ENO/WENO示例程序 +""" + +import os +import sys +import subprocess +from pathlib import Path + +# 添加当前目录到路径 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import BuildSystem +except ImportError: + print("错误: 找不到build.py,请确保在scripts目录中运行") + sys.exit(1) + +def run_example(): + """运行示例程序""" + builder = BuildSystem() + + print("\n" + "="*70) + print(" 运行ENO/WENO对比示例程序") + print("="*70 + "\n") + + # 检查是否已构建 + exe_path = builder.build_dir / "bin" / "Debug" / "example_eno_weno_comparison.exe" + + if not exe_path.exists(): + print("示例程序未构建,先构建项目...") + print("-"*50) + + # 使用简化的构建 + result = subprocess.run( + ["python", "build.py", "--no-tests", "--clean"], + cwd=builder.project_root / "scripts", + capture_output=True, + text=True + ) + + if result.returncode != 0: + print("构建失败:") + print(result.stderr) + return False + + # 运行示例程序 + print("运行示例程序...") + print("-"*50) + + try: + result = subprocess.run( + [str(exe_path)], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace' + ) + + print(result.stdout) + + if result.stderr: + print("标准错误输出:") + print(result.stderr) + + return result.returncode == 0 + + except Exception as e: + print(f"运行示例程序失败: {e}") + return False + +def main(): + """主函数""" + success = run_example() + + if success: + print("\n" + "="*70) + print(" ✓ 示例程序运行成功") + print("="*70) + return 0 + else: + print("\n" + "="*70) + print(" ✗ 示例程序运行失败") + print("="*70) + return 1 + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/run_step1.bat b/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/run_step1.bat new file mode 100644 index 00000000..0b6b1f17 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/run_step1.bat @@ -0,0 +1,39 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Step 1: Physics Modules Test +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python step1 script with full Intel environment support... +echo. + +python run_step1.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Step 1 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Step 1 completed successfully! +echo. +echo [INFO] Next step: Update config to include physics settings +echo [INFO] Run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/run_step1.py b/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/run_step1.py new file mode 100644 index 00000000..5e087a69 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/run_step1.py @@ -0,0 +1,319 @@ +#!/usr/bin/env python3 +""" +Step 1: Physics Modules Test +扩展build.py,专门用于测试物理模块 +""" + +import os +import sys +import subprocess +import time +from pathlib import Path + +# 添加当前目录到路径,以便导入build.py的类 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import IntelEnvironment, BuildSystem +except ImportError: + print("Error: Cannot import build.py. Make sure build.py is in the same directory.") + sys.exit(1) + +class Step1System(BuildSystem): + """Step 1 测试系统,继承自BuildSystem""" + + def __init__(self): + super().__init__() + self.test_name = "test_physics_minimal" + self.test_exe = None + + def find_test_executable(self): + """查找测试可执行文件""" + possible_paths = [ + self.build_dir / "bin" / "Debug" / f"{self.test_name}.exe", + self.build_dir / "Debug" / f"{self.test_name}.exe", + self.build_dir / f"{self.test_name}.exe", + self.build_dir / "bin" / f"{self.test_name}.exe", + ] + + for path in possible_paths: + if path.exists(): + self.test_exe = path + self.print_success(f"Found test executable: {path}") + return True + + # 如果没有找到,尝试搜索 + self.print_warning(f"Could not find {self.test_name}.exe") + self.print_info("Searching for test executables...") + + try: + result = subprocess.run( + ["dir", str(self.build_dir), "/s", "/b", "*.exe"], + capture_output=True, + text=True, + encoding='utf-8', + shell=True + ) + + if result.returncode == 0: + test_files = [line.strip() for line in result.stdout.split('\n') + if line and 'test_' in line.lower()] + + if test_files: + self.print_info("Found test files:") + for test_file in test_files: + self.print_info(f" {test_file}") + return False + except: + pass + + return False + + def run_test_with_intel_env(self): + """在Intel环境下运行测试""" + if not self.test_exe: + self.print_error("No test executable found") + return False + + self.print_step(1, 2, f"Running test: {self.test_exe.name}") + + try: + # 创建临时的批处理文件来运行测试(包含Intel环境) + import tempfile + + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + # 设置Intel环境 + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'echo [INFO] Intel environment configured\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'echo [WARNING] Intel environment not found\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + self.print_info(f"Command: {temp_bat}") + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower() or 'fail' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or 'pass' in line.lower() or '✓' in line: + print(f" \033[92m{line}\033[0m") + elif '=' in line or '---' in line: + print(f" \033[96m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + self.print_warning("Test stderr output:") + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + self.print_step(2, 2, "Test execution completed") + + if result.returncode == 0: + self.print_success("Test passed") + return True + else: + self.print_error(f"Test failed (exit code: {result.returncode})") + return False + + except Exception as e: + self.print_error(f"Failed to run test: {e}") + return False + + def build_project_if_needed(self, args): + """如果需要,构建项目""" + if args.no_build: + self.print_info("Skipping build (--no-build flag)") + return True + + self.print_step(1, 3, "Building project") + + # 调用父类的构建方法 + build_args = argparse.Namespace() + build_args.clean = args.clean + build_args.build_type = "Debug" + build_args.compiler = "ifx" + build_args.no_tests = True # 不运行所有测试 + build_args.jobs = os.cpu_count() + build_args.verbose = args.verbose + build_args.force = args.force + + # 清理构建目录 + if args.clean and self.build_dir.exists(): + self.print_info("Cleaning build directory...") + import shutil + try: + shutil.rmtree(self.build_dir) + self.print_success("Build directory cleaned") + except Exception as e: + self.print_error(f"Clean failed: {e}") + if not args.force: + return False + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 配置CMake + self.print_step(2, 3, "Configuring CMake") + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-DCMAKE_BUILD_TYPE=Debug", + "-T", "fortran=ifx", + ] + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + if not self.run_command(cmake_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + # 构建项目 + self.print_step(3, 3, "Building project") + build_cmd = [ + "cmake", + "--build", ".", + "--config", "Debug", + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + if not self.run_command(build_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + self.print_success("Build completed") + return True + + def run(self): + """运行Step 1测试""" + parser = argparse.ArgumentParser( + description="Step 1: Physics Modules Test", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认运行 + %(prog)s --clean # 清理后构建并测试 + %(prog)s --no-build # 只运行测试,不重新构建 + %(prog)s --verbose # 详细输出 + %(prog)s -j4 # 使用4个并行作业构建 + """ + ) + + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-build", action="store_true", + help="不重新构建,直接运行测试") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + + args = parser.parse_args() + + # 开始测试 + start_time = time.time() + + self.print_header("Step 1: Physics Modules Implementation Test") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 设置Intel环境 + self.print_step(1, 4, "Setting up Intel oneAPI environment") + if not self.setup_intel_environment(args): + if not args.force: + self.print_error("Intel environment setup failed") + return 1 + self.print_warning("Intel environment setup failed, continuing...") + + # 2. 构建项目(如果需要) + self.print_step(2, 4, "Building project if needed") + if not self.build_project_if_needed(args): + if not args.force: + return 1 + + # 3. 查找测试可执行文件 + self.print_step(3, 4, "Finding test executable") + if not self.find_test_executable(): + if not args.force: + return 1 + self.print_warning("Test executable not found, but continuing due to --force") + return 0 + + # 4. 运行测试 + self.print_step(4, 4, "Running physics module test") + test_passed = self.run_test_with_intel_env() + + # 生成报告 + test_time = time.time() - start_time + self.print_header("Step 1 Complete") + + print(f"测试: {'通过 ✓' if test_passed else '失败 ✗'}") + print(f"测试程序: {self.test_exe.name if self.test_exe else '未找到'}") + print(f"总耗时: {test_time:.1f}秒") + + if test_passed: + print(f"\n下一步: 更新配置以包含物理设置") + print(f"建议: 修改config.f90,添加physics相关字段") + return 0 + else: + if args.force: + self.print_warning("测试失败,但由于--force标志继续执行") + return 0 + return 1 + + except KeyboardInterrupt: + self.print_error("\n测试被用户中断") + return 1 + except Exception as e: + self.print_error(f"测试过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + system = Step1System() + return system.run() + +if __name__ == "__main__": + # 需要导入argparse + import argparse + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/run_step2.bat b/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/run_step2.bat new file mode 100644 index 00000000..9c1f62de --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/run_step2.bat @@ -0,0 +1,39 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Step 2: Configuration Physics Update +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python step2 script with full Intel environment support... +echo. + +python run_step2.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Step 2 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Step 2 completed successfully! +echo. +echo [INFO] Next step: Update component manager to support physics +echo [INFO] Run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/run_step2.py b/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/run_step2.py new file mode 100644 index 00000000..c16b7608 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/run_step2.py @@ -0,0 +1,284 @@ +#!/usr/bin/env python3 +""" +Step 2: Configuration Physics Update +测试配置模块的物理功能更新 +""" + +import os +import sys +import subprocess +import time +from pathlib import Path + +# 添加当前目录到路径,以便导入build.py的类 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import IntelEnvironment, BuildSystem +except ImportError: + print("Error: Cannot import build.py. Make sure build.py is in the same directory.") + sys.exit(1) + +class Step2System(BuildSystem): + """Step 2 测试系统,继承自BuildSystem""" + + def __init__(self): + super().__init__() + self.test_name = "test_config_physics" + self.test_exe = None + + def find_test_executable(self): + """查找测试可执行文件""" + possible_paths = [ + self.build_dir / "bin" / "Debug" / f"{self.test_name}.exe", + self.build_dir / "Debug" / f"{self.test_name}.exe", + self.build_dir / f"{self.test_name}.exe", + self.build_dir / "bin" / f"{self.test_name}.exe", + ] + + for path in possible_paths: + if path.exists(): + self.test_exe = path + self.print_success(f"Found test executable: {path}") + return True + + return False + + def run_test_with_intel_env(self): + """在Intel环境下运行测试""" + if not self.test_exe: + self.print_error("No test executable found") + return False + + self.print_step(1, 2, f"Running test: {self.test_exe.name}") + + try: + # 创建临时的批处理文件来运行测试(包含Intel环境) + import tempfile + + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + # 设置Intel环境 + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'echo [INFO] Intel environment configured\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'echo [WARNING] Intel environment not found\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + self.print_info(f"Command: {temp_bat}") + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower() or 'fail' in line.lower() or '✗' in line: + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or 'pass' in line.lower() or '✓' in line: + print(f" \033[92m{line}\033[0m") + elif '=' in line or '---' in line or '===' in line: + print(f" \033[96m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + self.print_warning("Test stderr output:") + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + self.print_step(2, 2, "Test execution completed") + + if result.returncode == 0: + self.print_success("Test passed") + return True + else: + self.print_error(f"Test failed (exit code: {result.returncode})") + return False + + except Exception as e: + self.print_error(f"Failed to run test: {e}") + return False + + def build_project(self, args): + """构建项目""" + if args.no_build: + self.print_info("Skipping build (--no-build flag)") + return True + + self.print_step(1, 3, "Building project") + + # 清理构建目录 + if args.clean and self.build_dir.exists(): + self.print_info("Cleaning build directory...") + import shutil + try: + shutil.rmtree(self.build_dir) + self.print_success("Build directory cleaned") + except Exception as e: + self.print_error(f"Clean failed: {e}") + if not args.force: + return False + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 配置CMake + self.print_step(2, 3, "Configuring CMake") + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-DCMAKE_BUILD_TYPE=Debug", + "-T", "fortran=ifx", + ] + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + if not self.run_command(cmake_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + # 构建项目 + self.print_step(3, 3, "Building project") + build_cmd = [ + "cmake", + "--build", ".", + "--config", "Debug", + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + if not self.run_command(build_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + self.print_success("Build completed") + return True + + def run(self): + """运行Step 2测试""" + import argparse + + parser = argparse.ArgumentParser( + description="Step 2: Configuration Physics Update", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认运行 + %(prog)s --clean # 清理后构建并测试 + %(prog)s --no-build # 只运行测试,不重新构建 + %(prog)s --verbose # 详细输出 + """ + ) + + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-build", action="store_true", + help="不重新构建,直接运行测试") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + + args = parser.parse_args() + + # 开始测试 + start_time = time.time() + + self.print_header("Step 2: Configuration Physics Update") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 设置Intel环境 + self.print_step(1, 4, "Setting up Intel oneAPI environment") + if not self.setup_intel_environment(args): + if not args.force: + self.print_error("Intel environment setup failed") + return 1 + self.print_warning("Intel environment setup failed, continuing...") + + # 2. 构建项目(如果需要) + self.print_step(2, 4, "Building project if needed") + if not self.build_project(args): + if not args.force: + return 1 + + # 3. 查找测试可执行文件 + self.print_step(3, 4, "Finding test executable") + if not self.find_test_executable(): + self.print_error(f"Test executable {self.test_name}.exe not found") + if not args.force: + return 1 + self.print_warning("Test executable not found, but continuing due to --force") + return 0 + + # 4. 运行测试 + self.print_step(4, 4, "Running configuration physics test") + test_passed = self.run_test_with_intel_env() + + # 生成报告 + test_time = time.time() - start_time + self.print_header("Step 2 Complete") + + print(f"测试: {'通过 ✓' if test_passed else '失败 ✗'}") + print(f"测试程序: {self.test_exe.name if self.test_exe else '未找到'}") + print(f"总耗时: {test_time:.1f}秒") + + if test_passed: + print(f"\n下一步: 更新组件管理器以支持物理模块") + print(f"建议: 修改component_manager.f90,添加physics组件创建") + return 0 + else: + if args.force: + self.print_warning("测试失败,但由于--flag继续执行") + return 0 + return 1 + + except KeyboardInterrupt: + self.print_error("\n测试被用户中断") + return 1 + except Exception as e: + self.print_error(f"测试过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + system = Step2System() + return system.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/test_integrated.py b/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/test_integrated.py new file mode 100644 index 00000000..91020a94 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/scripts/test_integrated.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# scripts/test_integrated.py +""" +测试集成求解器 +""" +import subprocess +import sys +import os +from pathlib import Path + +def run_test(): + """运行集成测试""" + # 构建项目 + print("构建项目...") + result = subprocess.run( + ["python", "scripts/build.py", "--no-tests", "--clean"], + capture_output=True, + text=True + ) + + if result.returncode != 0: + print("构建失败:") + print(result.stderr) + return False + + # 运行集成示例 + print("\n运行集成示例...") + exe_path = Path("build/bin/Debug/run_eno_weno_integrated.exe") + + if not exe_path.exists(): + print(f"可执行文件不存在: {exe_path}") + return False + + result = subprocess.run( + [str(exe_path)], + capture_output=True, + text=True, + encoding='utf-8' + ) + + print(result.stdout) + if result.stderr: + print("错误输出:") + print(result.stderr) + + return result.returncode == 0 + +if __name__ == "__main__": + success = run_test() + sys.exit(0 if success else 1) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03f/src/CMakeLists.txt new file mode 100644 index 00000000..ef816d13 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/CMakeLists.txt @@ -0,0 +1,41 @@ +# 修改后的完整版本: +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 按依赖顺序添加子目录 +add_subdirectory(base) # 1. 基础类型和精度 +add_subdirectory(core) # 2. 核心注册系统 +add_subdirectory(infrastructure) # 3. 基础设施(配置、网格、域、解) +add_subdirectory(physics) # 4. 物理模块(方程和问题) +add_subdirectory(numerics) # 5. 数值方法(重构器、通量、时间积分) +add_subdirectory(manager) # 6. 组件管理器和工厂 +add_subdirectory(solver) # 7. 求解器 + +# ==================== 新增:边界条件和初始条件模块 ==================== +message(STATUS "配置边界条件模块...") +add_subdirectory(boundary) + +message(STATUS "配置初始条件模块...") +add_subdirectory(initial_condition) + +# ==================== 新增:结果模块 ==================== +message(STATUS "配置结果模块...") + +add_library(results STATIC + results.f90 # 新增文件 +) + +target_link_libraries(results + PRIVATE + base + infrastructure + solver +) + +set_target_properties(results PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "集成求解器库配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/base/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03f/src/base/CMakeLists.txt new file mode 100644 index 00000000..74f4aa65 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/base/CMakeLists.txt @@ -0,0 +1,16 @@ +# src/base/CMakeLists.txt +message(STATUS "Configuring base module...") + +add_library(base STATIC + modules.f90 + precision.f90 # 新增 +) + +set_target_properties(base PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Base module configured") + +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/base/modules.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/base/modules.f90 new file mode 100644 index 00000000..43aaee24 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/base/modules.f90 @@ -0,0 +1,36 @@ +! src/base/modules.f90 +module base_modules + use, intrinsic :: iso_fortran_env, only: real64, int32 + implicit none + + public :: wp, ip, max_name_len, string_len, cfd_config_base, component_info + + integer, parameter :: wp = real64 + integer, parameter :: ip = int32 + integer, parameter :: string_len = 100 + integer, parameter :: max_name_len = 32 + + ! 基础配置类型 + type :: cfd_config_base + character(len=max_name_len) :: ic_type = "step" + character(len=max_name_len) :: recon_scheme = "eno" + character(len=max_name_len) :: flux_type = "rusanov" + integer(ip) :: rk_order = 1 + real(wp) :: wave_speed = 1.0_wp + real(wp) :: final_time = 0.625_wp + real(wp) :: dt = 0.025_wp + character(len=max_name_len) :: boundary_type = "periodic" + integer(ip) :: spatial_order = 2 + character(len=max_name_len) :: equation_type = "linear_advection" + character(len=max_name_len) :: problem_type = "linear_advection" + logical :: verbose = .true. + end type cfd_config_base + + ! 组件信息类型 + type :: component_info + character(len=max_name_len) :: category = "" + character(len=max_name_len) :: name = "" + integer(ip) :: order = 0 + end type component_info + +end module base_modules \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/base/precision.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/base/precision.f90 new file mode 100644 index 00000000..4ac5fd7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/base/precision.f90 @@ -0,0 +1,9 @@ +! src/base/precision.f90(简单版本) +module precision_module + use base_modules, only: wp, ip + implicit none + + ! 重新导出,确保兼容 + public :: wp, ip + +end module precision_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/boundary/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03f/src/boundary/CMakeLists.txt new file mode 100644 index 00000000..a9909f1e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/boundary/CMakeLists.txt @@ -0,0 +1,23 @@ +# src/boundary/CMakeLists.txt +message(STATUS "配置边界条件模块...") + +add_library(boundary STATIC + boundary_base.f90 # 基类 + periodic.f90 # 周期性边界 + dirichlet.f90 # Dirichlet边界 + neumann.f90 # Neumann边界 + factory.f90 # 工厂 +) + +target_link_libraries(boundary + PRIVATE + base + infrastructure + core # 需要注册系统 +) + +set_target_properties(boundary PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "边界条件模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/boundary/boundary_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/boundary/boundary_base.f90 new file mode 100644 index 00000000..2f7b1116 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/boundary/boundary_base.f90 @@ -0,0 +1,35 @@ +! src/boundary/boundary_base.f90 (修正版) +module boundary_base_module + use base_modules, only: wp, ip + implicit none + private + + type, abstract, public :: boundary_condition + character(len=:), allocatable :: name + contains + procedure(apply_interface), deferred :: apply + procedure :: get_name => bc_get_name + end type + + abstract interface + subroutine apply_interface(this, u, nghosts, ist, ied) + import :: boundary_condition, wp, ip + class(boundary_condition), intent(in) :: this + real(wp), intent(inout) :: u(:) + integer(ip), intent(in) :: nghosts, ist, ied + end subroutine apply_interface + end interface + +contains + + function bc_get_name(this) result(name) + class(boundary_condition), intent(in) :: this + character(len=:), allocatable :: name + if (allocated(this%name)) then + name = this%name + else + name = "unnamed" + end if + end function + +end module boundary_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/boundary/dirichlet.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/boundary/dirichlet.f90 new file mode 100644 index 00000000..e7647bea --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/boundary/dirichlet.f90 @@ -0,0 +1,55 @@ +! src/boundary/dirichlet.f90 +module dirichlet_boundary_module + use base_modules, only: wp, ip + use boundary_base_module, only: boundary_condition + implicit none + private + + type, extends(boundary_condition), public :: dirichlet_boundary + real(wp) :: left_value = 1.0_wp + real(wp) :: right_value = 2.0_wp + contains + procedure :: apply => dirichlet_apply + procedure :: set_values => dirichlet_set_values + end type + + interface dirichlet_boundary + module procedure create_dirichlet_boundary + end interface + +contains + + type(dirichlet_boundary) function create_dirichlet_boundary(left_val, right_val) result(this) + real(wp), optional, intent(in) :: left_val, right_val + + this%name = "dirichlet" + if (present(left_val)) this%left_value = left_val + if (present(right_val)) this%right_value = right_val + end function + + subroutine dirichlet_set_values(this, left_val, right_val) + class(dirichlet_boundary), intent(inout) :: this + real(wp), intent(in) :: left_val, right_val + this%left_value = left_val + this%right_value = right_val + end subroutine + + subroutine dirichlet_apply(this, u, nghosts, ist, ied) + class(dirichlet_boundary), intent(in) :: this + real(wp), intent(inout) :: u(:) + integer(ip), intent(in) :: nghosts, ist, ied + + integer :: i + + ! 左边界 + do i = 0, nghosts-1 + u(ist-1-i) = this%left_value + end do + + ! 右边界 + do i = 0, nghosts-1 + u(ied+i) = this%right_value + end do + end subroutine + +end module dirichlet_boundary_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/boundary/factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/boundary/factory.f90 new file mode 100644 index 00000000..012363b0 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/boundary/factory.f90 @@ -0,0 +1,41 @@ +! src/boundary/factory.f90 +module boundary_factory_module + use base_modules, only: wp, ip + use boundary_base_module, only: boundary_condition + use periodic_boundary_module, only: periodic_boundary + implicit none + private + + public :: create_boundary_condition, initialize_boundary_factory + +contains + + subroutine initialize_boundary_factory() + print *, "[BOUNDARY FACTORY] Boundary conditions placeholder" + end subroutine + + subroutine create_boundary_condition(bc_type, bc_instance, left_val, right_val) + character(len=*), intent(in) :: bc_type + class(boundary_condition), allocatable, intent(out) :: bc_instance + real(wp), optional, intent(in) :: left_val, right_val + + ! 暂时只实现周期性边界 + allocate(periodic_boundary :: bc_instance) + + select case (trim(bc_type)) + case ("periodic") + select type(bc => bc_instance) + type is (periodic_boundary) + bc%name = "periodic" + end select + + case default + print *, "[WARNING] Using periodic as default boundary" + select type(bc => bc_instance) + type is (periodic_boundary) + bc%name = "periodic" + end select + end select + end subroutine + +end module boundary_factory_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/boundary/neumann.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/boundary/neumann.f90 new file mode 100644 index 00000000..908869d9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/boundary/neumann.f90 @@ -0,0 +1,41 @@ +! src/boundary/neumann.f90 +module neumann_boundary_module + use base_modules, only: wp, ip + use boundary_base_module, only: boundary_condition + implicit none + private + + type, extends(boundary_condition), public :: neumann_boundary + contains + procedure :: apply => neumann_apply + end type + + interface neumann_boundary + module procedure create_neumann_boundary + end interface + +contains + + type(neumann_boundary) function create_neumann_boundary() result(this) + this%name = "neumann" + end function + + subroutine neumann_apply(this, u, nghosts, ist, ied) + class(neumann_boundary), intent(in) :: this + real(wp), intent(inout) :: u(:) + integer(ip), intent(in) :: nghosts, ist, ied + + integer :: i + + ! 左边界零梯度:u[ist-1-i] = u[ist+i] + do i = 0, nghosts-1 + u(ist-1-i) = u(ist+i) + end do + + ! 右边界零梯度:u[ied+i] = u[ied-1-i] + do i = 0, nghosts-1 + u(ied+i) = u(ied-1-i) + end do + end subroutine + +end module neumann_boundary_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/boundary/periodic.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/boundary/periodic.f90 new file mode 100644 index 00000000..1fad1f03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/boundary/periodic.f90 @@ -0,0 +1,44 @@ +! src/boundary/periodic.f90 (修正版) +module periodic_boundary_module + use base_modules, only: wp, ip + use boundary_base_module, only: boundary_condition + implicit none + private + + type, extends(boundary_condition), public :: periodic_boundary + contains + procedure :: apply => periodic_apply + procedure :: get_name => bc_get_name + end type + +contains + + subroutine periodic_apply(this, u, nghosts, ist, ied) + class(periodic_boundary), intent(in) :: this + real(wp), intent(inout) :: u(:) + integer(ip), intent(in) :: nghosts, ist, ied + + integer :: i + + ! 左ghost层:u[ist-1-i] = u[ied-1-i] + do i = 0, nghosts-1 + u(ist-1-i) = u(ied-1-i) + end do + + ! 右ghost层:u[ied+i] = u[ist+i] + do i = 0, nghosts-1 + u(ied+i) = u(ist+i) + end do + end subroutine + + function bc_get_name(this) result(name) + class(periodic_boundary), intent(in) :: this + character(len=:), allocatable :: name + if (allocated(this%name)) then + name = this%name + else + name = "periodic" + end if + end function + +end module periodic_boundary_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03f/src/core/CMakeLists.txt new file mode 100644 index 00000000..d8b8df06 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/core/CMakeLists.txt @@ -0,0 +1,14 @@ +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") + +add_library(core STATIC + registry.f90 +) + +target_link_libraries(core PRIVATE base) + +set_target_properties(core PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Core module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/core/factory_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/core/factory_base.f90 new file mode 100644 index 00000000..302418a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/core/factory_base.f90 @@ -0,0 +1,57 @@ +! src/core/factory_base.f90 +module factory_base_module + use base_modules, only: wp, ip + use registry_module, only: create_component, has_component + + implicit none + private + public :: wp, ip, factory_base, factory_create + + ! 工厂基类 + type :: factory_base + character(len=max_name_length) :: category = "" + contains + procedure :: create => factory_base_create + procedure :: get_available => factory_base_get_available + end type factory_base + + ! 便捷函数类型 + abstract interface + function factory_function_interface(category, name) result(instance) + import :: wp + character(len=*), intent(in) :: category, name + class(*), allocatable :: instance + end function factory_function_interface + end interface + +contains + + ! 创建工厂实例 + function factory_create(category) result(factory) + character(len=*), intent(in) :: category + type(factory_base) :: factory + factory%category = trim(category) + end function factory_create + + ! 工厂创建方法 + function factory_base_create(this, name) result(instance) + class(factory_base), intent(in) :: this + character(len=*), intent(in) :: name + class(*), allocatable :: instance + + instance = create_component(this%category, name) + end function factory_base_create + + ! 获取可用组件列表(简化版) + subroutine factory_base_get_available(this, names, count) + class(factory_base), intent(in) :: this + character(len=*), allocatable, intent(out) :: names(:) + integer(ip), intent(out) :: count + + ! 这里需要实现从注册表获取列表的逻辑 + ! 暂时返回空列表 + count = 0 + allocate(character(len=max_name_length) :: names(0)) + end subroutine factory_base_get_available + +end module factory_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/core/factory_integrated.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/core/factory_integrated.f90 new file mode 100644 index 00000000..c58864c9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/core/factory_integrated.f90 @@ -0,0 +1,41 @@ +! src/core/factory_integrated.f90 +module factory_integrated + use base_modules, only: wp, ip + use registry_module, only: register_component_simple + implicit none + private + + public :: register_all_components + +contains + + subroutine register_all_components() + ! 方程 + call register_component_simple("equation", "linear_advection") + + ! 问题 + call register_component_simple("problem", "linear_advection") + + ! 重构器 + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + + ! 通量 + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist_osher") + + ! 边界条件 + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + + ! 初始条件 + call register_component_simple("initial_condition", "step") + call register_component_simple("initial_condition", "sin") + call register_component_simple("initial_condition", "gaussian") + + print *, "[FACTORY] All components registered" + end subroutine + +end module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..a960167c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/core/physics_solver_integrated.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/core/physics_solver_integrated.f90 new file mode 100644 index 00000000..56840e67 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/core/physics_solver_integrated.f90 @@ -0,0 +1,127 @@ +! src/solver/physics_solver_integrated.f90 +module physics_solver_integrated + use base_modules, only: wp, ip + use config_module, only: cfd_config + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create + use registry_module, only: create_component + + implicit none + private + + type, public :: physics_solver_integrated + ! 核心组件 + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + + ! 物理组件 + class(*), allocatable :: equation + class(*), allocatable :: problem + + ! 数值组件 + class(*), allocatable :: reconstructor + class(*), allocatable :: flux_calculator + class(*), allocatable :: boundary_condition + class(*), allocatable :: residual_calculator + + contains + procedure :: initialize => solver_initialize + procedure :: run => solver_run + procedure :: cleanup => solver_cleanup + procedure, private :: create_components + procedure, private :: apply_boundary + end type + +contains + + subroutine solver_initialize(this) + class(physics_solver_integrated), intent(inout) :: this + + ! 创建域和解 + this%domain = domain_create(this%config, this%mesh) + this%solution = solution_create(this%domain) + + ! 创建所有组件 + call this%create_components() + + ! 应用初始条件 + call this%apply_initial_condition() + + print *, "[SOLVER] Initialized with physics integration" + end subroutine + + subroutine create_components(this) + class(physics_solver_integrated), intent(inout) :: this + + ! 创建方程 + call create_component("equation", "linear_advection", this%config, this%equation) + + ! 创建问题 + call create_component("problem", "linear_advection", this%config, this%problem) + + ! 创建数值组件 + call create_component("reconstructor", this%config%recon_scheme, & + this%config, this%reconstructor) + call create_component("flux", this%config%flux_type, & + this%config, this%flux_calculator) + call create_component("boundary", this%config%boundary_type, & + this%cfd_context(), this%boundary_condition) + end subroutine + + subroutine apply_initial_condition(this) + class(physics_solver_integrated), intent(inout) :: this + + ! 通过问题创建初始条件 + select type(prob => this%problem) + type is (linear_advection_problem) + class(*), allocatable :: ic + call prob%create_ic(this%config, ic) + + ! 应用初始条件到解 + select type(ic_inst => ic) + type is (step_function_ic) + call ic_inst%apply(this%solution) + end select + end select + end subroutine + + function cfd_context(this) result(ctx) + class(physics_solver_integrated), intent(in) :: this + type(cfd_context_type) :: ctx + + ! 创建包含求解器所有组件的上下文 + ctx%config => this%config + ctx%domain => this%domain + ctx%solution => this%solution + ctx%equation => this%equation + end function + + subroutine solver_run(this, final_time) + class(physics_solver_integrated), intent(inout) :: this + real(wp), intent(in) :: final_time + + real(wp) :: t, dt, dt_original + integer :: step + + dt_original = this%config%dt + t = 0.0_wp + step = 0 + + do while (t < final_time - 1e-12_wp) + dt = min(this%config%dt, final_time - t) + + ! 时间步进(需要实现) + ! call this%time_step(dt) + + t = t + dt + step = step + 1 + end do + + this%config%dt = dt_original + print *, "[SOLVER] Completed at t = ", t, ", steps = ", step + end subroutine + +end module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/core/registry.f90 new file mode 100644 index 00000000..d155aa19 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/core/registry.f90 @@ -0,0 +1,257 @@ +! src/core/registry.f90 (更新版) +module registry_module + use base_modules, only: wp, ip, max_name_len, component_info + + implicit none + private + + ! 明确公开所有需要的接口 + public :: wp, ip ! 类型参数 + public :: component_info ! 类型 + public :: registry_init, registry_cleanup ! 初始化/清理 + public :: register_component_simple ! 注册组件 + public :: has_component_simple ! 检查组件 + public :: list_components ! 列出组件 + public :: registry_is_initialized ! 检查初始化状态 + public :: registry_get_size ! 获取大小 + public :: initialize_default_components ! 新增:初始化默认组件 + + ! 全局注册表 + type :: component_registry + type(component_info), allocatable :: components(:) + integer(ip) :: count = 0 + integer(ip) :: capacity = 100 + logical :: initialized = .false. + logical :: verbose = .true. + logical :: default_components_added = .false. ! 新增:标记是否已添加默认组件 + end type component_registry + + type(component_registry) :: registry + +contains + + ! ==================== 公共API ==================== + + subroutine registry_init(verbose) + logical, optional, intent(in) :: verbose + + if (registry%initialized) then + if (registry%verbose) then + print *, "[REGISTRY] Already initialized" + end if + return + end if + + if (present(verbose)) then + registry%verbose = verbose + end if + + allocate(registry%components(registry%capacity)) + registry%initialized = .true. + + if (registry%verbose) then + print *, "[REGISTRY] Initialized with capacity:", registry%capacity + end if + end subroutine registry_init + + subroutine registry_cleanup() + if (allocated(registry%components)) then + deallocate(registry%components) + end if + registry%initialized = .false. + registry%count = 0 + registry%default_components_added = .false. ! 重置标记 + + if (registry%verbose) then + print *, "[REGISTRY] Cleaned up" + end if + end subroutine registry_cleanup + + ! 新增:初始化默认组件 + subroutine initialize_default_components() + if (.not. registry%initialized) then + call registry_init() + end if + + if (registry%default_components_added) then + if (registry%verbose) then + print *, "[REGISTRY] Default components already added" + end if + return + end if + + ! 注册重构器 + call register_component_simple("reconstructor", "eno", order=3) + call register_component_simple("reconstructor", "weno3", order=3) + call register_component_simple("reconstructor", "weno5", order=5) + + ! 注册通量计算器 + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + + ! 注册边界条件 + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + + ! 注册时间积分器 + call register_component_simple("integrator", "rk1", order=1) + call register_component_simple("integrator", "rk2", order=2) + call register_component_simple("integrator", "rk3", order=3) + + ! 注册方程 + call register_component_simple("equation", "linear_advection") + + ! 注册问题 + call register_component_simple("problem", "linear_advection") + + ! 注册初始条件 + call register_component_simple("initial_condition", "step") + call register_component_simple("initial_condition", "sin") + call register_component_simple("initial_condition", "gaussian") + + registry%default_components_added = .true. + + if (registry%verbose) then + print *, "[REGISTRY] Default components registered" + print *, "[REGISTRY] Total components:", registry%count + end if + end subroutine initialize_default_components + + subroutine register_component_simple(category, name, order) + character(len=*), intent(in) :: category, name + integer(ip), optional, intent(in) :: order + + integer(ip) :: i + type(component_info) :: info + + if (.not. registry%initialized) then + call registry_init() + end if + + ! 检查是否已存在 + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + if (registry%verbose) then + print *, "[WARN] Overwriting component: ", trim(category), ".", trim(name) + end if + + ! 更新 + if (present(order)) then + registry%components(i)%order = order + else + registry%components(i)%order = 0 + end if + return + end if + end do + + ! 扩展数组 + if (registry%count >= registry%capacity) then + call expand_registry() + end if + + ! 添加新组件 + registry%count = registry%count + 1 + + info%category = trim(category) + info%name = trim(name) + info%order = 0 + if (present(order)) then + info%order = order + end if + + registry%components(registry%count) = info + + if (registry%verbose) then + print *, "[OK] Registered simple: ", trim(category), ".", trim(name) + end if + end subroutine register_component_simple + + logical function has_component_simple(category, name) + character(len=*), intent(in) :: category, name + + integer(ip) :: i + + has_component_simple = .false. + + if (.not. registry%initialized) return + + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + has_component_simple = .true. + return + end if + end do + end function has_component_simple + + subroutine list_components(category) + character(len=*), optional, intent(in) :: category + + integer(ip) :: i, count + + if (.not. registry%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + if (registry%count == 0) then + print *, "[INFO] No components registered" + return + end if + + count = 0 + print *, "=== Registry Contents ===" + do i = 1, registry%count + if (.not. present(category) .or. & + trim(registry%components(i)%category) == trim(category)) then + call print_component_info(registry%components(i)) + count = count + 1 + end if + end do + + print *, "Total:", count, "components" + print *, "==========================" + end subroutine list_components + + ! ==================== 新增函数 ==================== + + logical function registry_is_initialized() + ! 检查注册表是否已初始化 + registry_is_initialized = registry%initialized + end function registry_is_initialized + + integer(ip) function registry_get_size() + ! 获取注册表中的组件数量 + registry_get_size = registry%count + end function registry_get_size + + ! ==================== 内部辅助函数 ==================== + + subroutine expand_registry() + type(component_info), allocatable :: temp(:) + + registry%capacity = registry%capacity * 2 + allocate(temp(registry%capacity)) + temp(1:registry%count) = registry%components(1:registry%count) + call move_alloc(temp, registry%components) + + if (registry%verbose) then + print *, "[INFO] Registry expanded to capacity:", registry%capacity + end if + end subroutine expand_registry + + subroutine print_component_info(info) + type(component_info), intent(in) :: info + + if (info%order > 0) then + print *, " [", trim(info%category), ".", trim(info%name), & + " (order:", info%order, ")]" + else + print *, " [", trim(info%category), ".", trim(info%name), "]" + end if + end subroutine print_component_info + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/core/registry_initializer.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/core/registry_initializer.f90 new file mode 100644 index 00000000..44023d1d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/core/registry_initializer.f90 @@ -0,0 +1,39 @@ +! src/core/registry_initializer.f90 (新增文件) +module registry_initializer_module + use registry_module, only: register_component_simple + implicit none + private + public :: initialize_default_registry + +contains + + subroutine initialize_default_registry() + ! 注册重构器 + call register_component_simple("reconstructor", "eno", order=3) + call register_component_simple("reconstructor", "weno3", order=3) + call register_component_simple("reconstructor", "weno5", order=5) + + ! 注册通量计算器 + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + + ! 注册边界条件 + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + + ! 注册时间积分器 + call register_component_simple("integrator", "rk1", order=1) + call register_component_simple("integrator", "rk2", order=2) + call register_component_simple("integrator", "rk3", order=3) + + ! 注册方程 + call register_component_simple("equation", "linear_advection") + + ! 注册问题 + call register_component_simple("problem", "linear_advection") + + print *, "[REGISTRY] Default components registered" + end subroutine initialize_default_registry + +end module registry_initializer_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03f/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..70cbbd2f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/infrastructure/CMakeLists.txt @@ -0,0 +1,17 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "Configuring infrastructure module...") + +add_library(infrastructure STATIC + config.f90 + mesh.f90 + domain.f90 # 新增 + solution.f90 # 新增 +) + +target_link_libraries(infrastructure PRIVATE base) + +set_target_properties(infrastructure PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Infrastructure module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/infrastructure/config.f90 new file mode 100644 index 00000000..7586a1a5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/infrastructure/config.f90 @@ -0,0 +1,144 @@ +! src/infrastructure/config.f90 (修复版) +module config_module + use base_modules, only: wp, ip, max_name_len, cfd_config_base + + implicit none + public :: wp, ip, cfd_config, config_print, config_with_reconstruction + + ! 扩展配置类型 - 添加物理相关字段 + type, extends(cfd_config_base) :: cfd_config + ! 物理参数 + real(wp) :: left_boundary_value = 1.0_wp + real(wp) :: right_boundary_value = 2.0_wp + real(wp) :: domain_length = 2.0_wp + + ! 新增:物理模块相关配置 + real(wp) :: pulse_center = 0.5_wp ! 高斯脉冲中心 + real(wp) :: pulse_width = 0.1_wp ! 高斯脉冲宽度 + logical :: enable_physics = .true. ! 是否启用物理模块 + contains + ! 新增:物理相关配置方法 + procedure :: set_physics_parameters + procedure :: get_physics_info + end type cfd_config + +contains + + subroutine config_print(cfg) + type(cfd_config), intent(in) :: cfg + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(cfg%ic_type) + print *, "Reconstruction: ", trim(cfg%recon_scheme), " (order:", cfg%spatial_order, ")" + print *, "Flux type: ", trim(cfg%flux_type) + print *, "Time integration: RK", cfg%rk_order + print *, "Wave speed: ", cfg%wave_speed + print *, "Final time: ", cfg%final_time + print *, "Time step: ", cfg%dt + print *, "Boundary: ", trim(cfg%boundary_type) + + ! 新增:物理配置信息 + print *, "--- Physics Configuration ---" + print *, "Equation type: ", trim(cfg%equation_type) + print *, "Problem type: ", trim(cfg%problem_type) + print *, "Domain length: ", cfg%domain_length + print *, "Physics enabled: ", cfg%enable_physics + + if (cfg%ic_type == "gaussian") then + print *, "Pulse center: ", cfg%pulse_center + print *, "Pulse width: ", cfg%pulse_width + end if + + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(cfg, scheme, order) + type(cfd_config), intent(inout) :: cfg + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + integer :: i + + ! 转换为小写 + cfg%recon_scheme = scheme + do i = 1, len_trim(cfg%recon_scheme) + if (cfg%recon_scheme(i:i) >= 'A' .and. cfg%recon_scheme(i:i) <= 'Z') then + cfg%recon_scheme(i:i) = char(ichar(cfg%recon_scheme(i:i)) + 32) + end if + end do + + ! 设置阶数 + if (present(order)) then + cfg%spatial_order = order + else + if (index(cfg%recon_scheme, 'weno') > 0) then + cfg%spatial_order = 5 + else if (trim(cfg%recon_scheme) == 'eno') then + cfg%spatial_order = 3 + end if + end if + + if (cfg%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(cfg%recon_scheme), & + " Order: ", cfg%spatial_order + end if + end subroutine config_with_reconstruction + + ! ========== 新增:物理参数设置方法 ========== + + subroutine set_physics_parameters(this, equation_type, problem_type, & + domain_length, enable_physics) + class(cfd_config), intent(inout) :: this + character(len=*), intent(in), optional :: equation_type, problem_type + real(wp), intent(in), optional :: domain_length + logical, intent(in), optional :: enable_physics + + if (present(equation_type)) then + this%equation_type = trim(equation_type) + if (this%verbose) then + print *, "[CONFIG] Set equation type: ", trim(this%equation_type) + end if + end if + + if (present(problem_type)) then + this%problem_type = trim(problem_type) + if (this%verbose) then + print *, "[CONFIG] Set problem type: ", trim(this%problem_type) + end if + end if + + if (present(domain_length)) then + this%domain_length = domain_length + if (this%verbose) then + print *, "[CONFIG] Set domain length: ", this%domain_length + end if + end if + + if (present(enable_physics)) then + this%enable_physics = enable_physics + if (this%verbose) then + print *, "[CONFIG] Physics module enabled: ", this%enable_physics + end if + end if + end subroutine set_physics_parameters + + subroutine get_physics_info(this) + class(cfd_config), intent(in) :: this + + print *, "=== Physics Configuration Info ===" + print *, "Equation type: ", trim(this%equation_type) + print *, "Problem type: ", trim(this%problem_type) + print *, "Domain length: ", this%domain_length + print *, "Wave speed: ", this%wave_speed + print *, "Physics enabled: ", this%enable_physics + + if (this%ic_type == "gaussian") then + print *, "Pulse parameters:" + print *, " Center: ", this%pulse_center + print *, " Width: ", this%pulse_width + end if + + print *, "==================================" + end subroutine get_physics_info + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/infrastructure/domain.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/infrastructure/domain.f90 new file mode 100644 index 00000000..c3662f03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/infrastructure/domain.f90 @@ -0,0 +1,102 @@ +! src/infrastructure/domain.f90 +module domain_module + use base_modules, only: wp, ip, max_name_len + use config_module, only: cfd_config + use mesh_module, only: mesh_type + + implicit none + private + public :: wp, ip, domain_type, domain_create, is_physical_cell + + type :: domain_type + type(cfd_config), pointer :: config => null() + type(mesh_type), pointer :: mesh => null() + integer(ip) :: nghosts = 0 + integer(ip) :: ist = 1 ! 物理区域起始索引(1-based) + integer(ip) :: ied = 1 ! 物理区域结束索引(exclusive) + integer(ip) :: ntcells = 0 ! 总单元数(含ghost) + contains + procedure :: print_info => domain_print_info + procedure :: get_physical_indices => domain_get_physical_indices + end type domain_type + +contains + + function domain_create(config, mesh) result(domain) + type(cfd_config), target, intent(in) :: config + type(mesh_type), target, intent(in) :: mesh + type(domain_type) :: domain + + domain%config => config + domain%mesh => mesh + + ! 计算ghost层数(参考Julia的_calc_nghosts) + domain%nghosts = calc_nghosts(config) + domain%ist = domain%nghosts + 1 + domain%ied = domain%ist + mesh%ncells + domain%ntcells = mesh%ncells + 2 * domain%nghosts + + if (config%verbose) then + print *, "[DOMAIN] Created:" + print *, " Ghost layers: ", domain%nghosts + print *, " Physical cells: ", domain%ist, " to ", domain%ied - 1 + print *, " Total cells: ", domain%ntcells + end if + end function domain_create + + function calc_nghosts(config) result(nghosts) + type(cfd_config), intent(in) :: config + integer(ip) :: nghosts + + character(len=max_name_len) :: scheme + + scheme = config%recon_scheme + + if (scheme == "eno") then + nghosts = config%spatial_order + else if (index(scheme, "weno") > 0) then + nghosts = config%spatial_order / 2 + 1 + else + print *, "[WARNING] Unknown scheme, using default nghosts=2" + nghosts = 2 + end if + + if (nghosts <= 0) then + print *, "[ERROR] Invalid nghosts: ", nghosts + nghosts = 2 + end if + end function calc_nghosts + + logical function is_physical_cell(this, idx) + class(domain_type), intent(in) :: this + integer(ip), intent(in) :: idx + is_physical_cell = (idx >= this%ist .and. idx < this%ied) + end function is_physical_cell + + function domain_get_physical_indices(this) result(indices) + class(domain_type), intent(in) :: this + integer(ip), allocatable :: indices(:) + integer(ip) :: i, count + + count = this%ied - this%ist + allocate(indices(count)) + + do i = 1, count + indices(i) = this%ist + i - 1 + end do + end function domain_get_physical_indices + + subroutine domain_print_info(this) + class(domain_type), intent(in) :: this + + print *, "=== Domain Information ===" + print *, "Configuration: ", trim(this%config%recon_scheme), & + " order ", this%config%spatial_order + print *, "Ghost layers: ", this%nghosts + print *, "Physical cells: ", this%ist, " to ", this%ied - 1 + print *, "Total cells: ", this%ntcells + print *, "Mesh cells: ", this%mesh%ncells + print *, "==========================" + end subroutine domain_print_info + +end module domain_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..f810f3a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/infrastructure/mesh.f90 @@ -0,0 +1,73 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use base_modules, only: wp, ip + + implicit none + public :: wp, ip, mesh_type, mesh_init, mesh_print_info + + ! 网格类型 + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer(ip) :: ncells = 40 + integer(ip) :: nnodes + integer(ip) :: nx + real(wp), allocatable :: x(:) ! 节点坐标 + real(wp), allocatable :: xcc(:) ! 单元中心坐标 + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer(ip), optional, intent(in) :: ncells + + integer(ip) :: i + + ! 设置参数 + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! 计算 + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! 分配内存 + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! 生成节点坐标 + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! 生成单元中心坐标 + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== Mesh Information ===" + print *, "Domain: [", this%xmin, ", ", this%xmax, "]" + print *, "Cells: ", this%ncells + print *, "Nodes: ", this%nnodes + print *, "dx: ", this%dx + print *, "L: ", this%L + print *, "========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/infrastructure/solution.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/infrastructure/solution.f90 new file mode 100644 index 00000000..ce88fd8a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/infrastructure/solution.f90 @@ -0,0 +1,131 @@ +! src/infrastructure/solution.f90 +module solution_module + use base_modules, only: wp, ip + use domain_module, only: domain_type + + implicit none + private + public :: wp, ip, solution_type, solution_create, solution_reset + + type :: solution_type + type(domain_type), pointer :: domain => null() + real(wp), allocatable :: u(:) ! 当前解(含ghost) + real(wp), allocatable :: un(:) ! 旧解 + real(wp), allocatable :: q_face_left(:) ! 左界面值 + real(wp), allocatable :: q_face_right(:)! 右界面值 + real(wp), allocatable :: flux(:) ! 通量 + real(wp), allocatable :: res(:) ! 残差 + contains + procedure :: initialize => solution_initialize + procedure :: update_old_field => solution_update_old_field + procedure :: print_info => solution_print_info + procedure :: reset => solution_reset_instance + end type solution_type + +contains + + function solution_create(domain) result(solution) + type(domain_type), target, intent(in) :: domain + type(solution_type) :: solution + + integer(ip) :: ncells, nnodes, ntcells + + solution%domain => domain + + ncells = domain%mesh%ncells + nnodes = domain%mesh%nnodes + ntcells = domain%ntcells + + ! 分配数组(与Julia solution.jl一致) + allocate(solution%u(ntcells), source=0.0_wp) + allocate(solution%un(ntcells), source=0.0_wp) + allocate(solution%q_face_left(nnodes), source=0.0_wp) + allocate(solution%q_face_right(nnodes), source=0.0_wp) + allocate(solution%flux(nnodes), source=0.0_wp) + allocate(solution%res(ncells), source=0.0_wp) + + if (domain%config%verbose) then + print *, "[SOLUTION] Created:" + print *, " u size: ", size(solution%u), " (with ghosts)" + print *, " flux size: ", size(solution%flux) + print *, " res size: ", size(solution%res) + end if + end function solution_create + + subroutine solution_initialize(this, initial_values) + class(solution_type), intent(inout) :: this + real(wp), intent(in), optional :: initial_values(:) + + integer(ip) :: i, idx + type(domain_type), pointer :: domain + + domain => this%domain + + if (present(initial_values)) then + ! 应用初始值到物理区域 + do i = domain%ist, domain%ied - 1 + idx = i - domain%ist + 1 + if (idx <= size(initial_values)) then + this%u(i) = initial_values(idx) + end if + end do + else + ! 默认为0 + this%u = 0.0_wp + end if + + ! 同步旧场(与Julia的update_old_field一致) + call this%update_old_field() + + if (domain%config%verbose) then + print *, "[SOLUTION] Initialized" + print *, " u range: ", minval(this%u), " to ", maxval(this%u) + end if + end subroutine solution_initialize + + subroutine solution_update_old_field(this) + class(solution_type), intent(inout) :: this + this%un = this%u ! 与Julia的 un .= u 一致 + end subroutine solution_update_old_field + + subroutine solution_reset_instance(this) + class(solution_type), intent(inout) :: this + call solution_reset(this) + end subroutine solution_reset_instance + + subroutine solution_reset(solution) + type(solution_type), intent(inout) :: solution + + if (allocated(solution%u)) solution%u = 0.0_wp + if (allocated(solution%un)) solution%un = 0.0_wp + if (allocated(solution%q_face_left)) solution%q_face_left = 0.0_wp + if (allocated(solution%q_face_right)) solution%q_face_right = 0.0_wp + if (allocated(solution%flux)) solution%flux = 0.0_wp + if (allocated(solution%res)) solution%res = 0.0_wp + + if (associated(solution%domain) .and. solution%domain%config%verbose) then + print *, "[SOLUTION] Reset" + end if + end subroutine solution_reset + + subroutine solution_print_info(this) + class(solution_type), intent(in) :: this + + print *, "=== Solution Information ===" + print *, "Arrays:" + print *, " u: ", size(this%u), " elements" + print *, " un: ", size(this%un), " elements" + print *, " q_face_left: ", size(this%q_face_left), " elements" + print *, " q_face_right: ", size(this%q_face_right), " elements" + print *, " flux: ", size(this%flux), " elements" + print *, " res: ", size(this%res), " elements" + + if (allocated(this%u)) then + print *, "Values:" + print *, " u min/max: ", minval(this%u), maxval(this%u) + print *, " un min/max: ", minval(this%un), maxval(this%un) + end if + print *, "============================" + end subroutine solution_print_info + +end module solution_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/initial_condition/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03f/src/initial_condition/CMakeLists.txt new file mode 100644 index 00000000..01fbfa47 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/initial_condition/CMakeLists.txt @@ -0,0 +1,23 @@ +# src/initial_condition/CMakeLists.txt +message(STATUS "配置初始条件模块...") + +add_library(initial_condition STATIC + ic_base.f90 # 基类 + step.f90 # 阶跃函数 + sine.f90 # 正弦波 + gaussian.f90 # 高斯脉冲 + factory.f90 # 工厂 +) + +target_link_libraries(initial_condition + PRIVATE + base + infrastructure + core # 需要注册系统 +) + +set_target_properties(initial_condition PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "初始条件模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/initial_condition/factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/initial_condition/factory.f90 new file mode 100644 index 00000000..7fbdd522 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/initial_condition/factory.f90 @@ -0,0 +1,31 @@ +! src/initial_condition/factory.f90 +module ic_factory_module + use base_modules, only: wp, ip + use ic_base_module, only: initial_condition + use step_ic_module, only: step_function_ic + implicit none + private + + public :: create_initial_condition, initialize_ic_factory + +contains + + subroutine initialize_ic_factory() + print *, "[IC FACTORY] Initial conditions placeholder" + end subroutine + + subroutine create_initial_condition(ic_type, ic_instance) + character(len=*), intent(in) :: ic_type + class(initial_condition), allocatable, intent(out) :: ic_instance + + ! 暂时只实现步函数 + allocate(step_function_ic :: ic_instance) + + select type(ic => ic_instance) + type is (step_function_ic) + ! 使用默认构造函数 + ic%name = "step" + end select + end subroutine + +end module ic_factory_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/initial_condition/gaussian.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/initial_condition/gaussian.f90 new file mode 100644 index 00000000..ed2b625b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/initial_condition/gaussian.f90 @@ -0,0 +1,62 @@ +! src/initial_condition/gaussian.f90 +module gaussian_ic_module + use base_modules, only: wp, ip + use ic_base_module, only: initial_condition + use solution_module, only: solution_type + use domain_module, only: domain_type + implicit none + private + + type, extends(initial_condition), public :: gaussian_pulse_ic + contains + procedure :: evaluate_at => gaussian_evaluate_at + procedure :: apply => gaussian_apply + end type + + interface gaussian_pulse_ic + module procedure create_gaussian_pulse_ic + end interface + +contains + + type(gaussian_pulse_ic) function create_gaussian_pulse_ic() result(this) + this%name = "gaussian" + end function + + function gaussian_evaluate_at(this, x) result(u) + class(gaussian_pulse_ic), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp) :: u(size(x)) + + integer :: i + real(wp) :: center, width + + center = 0.5_wp ! 脉冲中心 + width = 0.1_wp ! 脉冲宽度 + + do i = 1, size(x) + u(i) = exp(-((x(i) - center) / width)**2) + end do + end function + + subroutine gaussian_apply(this, solution) + class(gaussian_pulse_ic), intent(in) :: this + type(solution_type), intent(inout) :: solution + + integer :: i, idx + real(wp), allocatable :: u0(:) + type(domain_type), pointer :: domain + + domain => solution%domain + + ! 评估初始条件 + u0 = this%evaluate_at(domain%mesh%xcc) + + ! 应用到物理区域 + do i = domain%ist, domain%ied - 1 + idx = i - domain%ist + 1 + solution%u(i) = u0(idx) + end do + end subroutine + +end module gaussian_ic_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/initial_condition/ic_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/initial_condition/ic_base.f90 new file mode 100644 index 00000000..4a7845fe --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/initial_condition/ic_base.f90 @@ -0,0 +1,43 @@ +! src/initial_condition/ic_base.f90 +module ic_base_module + use base_modules, only: wp, ip + use solution_module, only: solution_type + implicit none + private + + type, abstract, public :: initial_condition + character(len=:), allocatable :: name + contains + procedure :: get_name => ic_get_name + procedure(evaluate_interface), deferred :: evaluate_at + procedure(apply_interface), deferred :: apply + end type + + abstract interface + function evaluate_interface(this, x) result(u) + import :: initial_condition, wp + class(initial_condition), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp) :: u(size(x)) + end function evaluate_interface + + subroutine apply_interface(this, solution) + import :: initial_condition, solution_type + class(initial_condition), intent(in) :: this + type(solution_type), intent(inout) :: solution + end subroutine apply_interface + end interface + +contains + + function ic_get_name(this) result(name) + class(initial_condition), intent(in) :: this + character(len=:), allocatable :: name + if (allocated(this%name)) then + name = this%name + else + name = "unnamed" + end if + end function + +end module ic_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/initial_condition/sine.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/initial_condition/sine.f90 new file mode 100644 index 00000000..f9f1028c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/initial_condition/sine.f90 @@ -0,0 +1,62 @@ +! src/initial_condition/sine.f90 +module sine_ic_module + use base_modules, only: wp, ip + use ic_base_module, only: initial_condition + use solution_module, only: solution_type + use domain_module, only: domain_type + implicit none + private + + type, extends(initial_condition), public :: sine_wave_ic + contains + procedure :: evaluate_at => sine_evaluate_at + procedure :: apply => sine_apply + end type + + interface sine_wave_ic + module procedure create_sine_wave_ic + end interface + +contains + + type(sine_wave_ic) function create_sine_wave_ic() result(this) + this%name = "sin" + end function + + function sine_evaluate_at(this, x) result(u) + class(sine_wave_ic), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp) :: u(size(x)) + + integer :: i + real(wp) :: L + + ! 假设域长度,可以根据需要调整 + L = 2.0_wp ! 默认域长度 + + do i = 1, size(x) + u(i) = sin(2.0_wp * 3.141592653589793_wp * x(i) / L) + end do + end function + + subroutine sine_apply(this, solution) + class(sine_wave_ic), intent(in) :: this + type(solution_type), intent(inout) :: solution + + integer :: i, idx + real(wp), allocatable :: u0(:) + type(domain_type), pointer :: domain + + domain => solution%domain + + ! 评估初始条件 + u0 = this%evaluate_at(domain%mesh%xcc) + + ! 应用到物理区域 + do i = domain%ist, domain%ied - 1 + idx = i - domain%ist + 1 + solution%u(i) = u0(idx) + end do + end subroutine + +end module sine_ic_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/initial_condition/step.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/initial_condition/step.f90 new file mode 100644 index 00000000..dca60265 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/initial_condition/step.f90 @@ -0,0 +1,62 @@ +! src/initial_condition/step.f90 +module step_ic_module + use base_modules, only: wp, ip + use ic_base_module, only: initial_condition + use solution_module, only: solution_type + use domain_module, only: domain_type + implicit none + private + + type, extends(initial_condition), public :: step_function_ic + contains + procedure :: evaluate_at => step_evaluate_at + procedure :: apply => step_apply + end type + + interface step_function_ic + module procedure create_step_function_ic + end interface + +contains + + type(step_function_ic) function create_step_function_ic() result(this) + this%name = "step" + end function + + function step_evaluate_at(this, x) result(u) + class(step_function_ic), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp) :: u(size(x)) + + integer :: i + + do i = 1, size(x) + if (x(i) >= 0.5_wp .and. x(i) <= 1.0_wp) then + u(i) = 2.0_wp + else + u(i) = 1.0_wp + end if + end do + end function + + subroutine step_apply(this, solution) + class(step_function_ic), intent(in) :: this + type(solution_type), intent(inout) :: solution + + integer :: i, idx + real(wp), allocatable :: u0(:) + type(domain_type), pointer :: domain + + domain => solution%domain + + ! 评估初始条件 + u0 = this%evaluate_at(domain%mesh%xcc) + + ! 应用到物理区域 + do i = domain%ist, domain%ied - 1 + idx = i - domain%ist + 1 + solution%u(i) = u0(idx) + end do + end subroutine + +end module step_ic_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/manager/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03f/src/manager/CMakeLists.txt new file mode 100644 index 00000000..00c8bf49 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/manager/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/manager/CMakeLists.txt +message(STATUS "配置管理器模块...") + +# 创建管理器库 +add_library(manager STATIC + component_manager.f90 + component_factory.f90 +) + +# 明确依赖关系:管理器依赖所有其他模块 +target_link_libraries(manager + PRIVATE + core + infrastructure + reconstructor + flux +) + +# 设置模块输出目录 +set_target_properties(manager PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "管理器模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/manager/component_factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/manager/component_factory.f90 new file mode 100644 index 00000000..bafceddb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/manager/component_factory.f90 @@ -0,0 +1,127 @@ +! src/manager/component_factory.f90 +module component_factory_module + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use weno5_reconstructor_module, only: weno5_reconstructor + use rusanov_flux_module, only: rusanov_flux + + implicit none + private + public :: wp, create_reconstructor, create_flux_calculator + + ! 定义wp以保持兼容性 + integer, parameter :: wp = real64 + + ! 错误代码 + integer, parameter :: CM_SUCCESS = 0 + integer, parameter :: CM_ERROR_UNKNOWN_SCHEME = 1 + integer, parameter :: CM_ERROR_UNKNOWN_FLUX = 2 + integer, parameter :: CM_ERROR_INVALID_ORDER = 3 + +contains + + ! ==================== 重构器创建 ==================== + + function create_reconstructor(config, status) result(recon) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(reconstructor_base), allocatable :: recon + + character(len=20) :: scheme + integer :: order, error_code + + scheme = trim(adjustl(config%recon_scheme)) + order = config%spatial_order + + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating reconstructor: ", scheme, " order=", order + end if + + ! 处理"weno"作为WENO5的别名 + if (scheme == "weno" .and. order == 5) then + scheme = "weno5" + end if + + select case(scheme) + case('eno') + allocate(eno_reconstructor :: recon) + select type(recon) + type is(eno_reconstructor) + recon%order = order + end select + + case('weno3') + allocate(weno3_reconstructor :: recon) + select type(recon) + type is(weno3_reconstructor) + recon%order = order + end select + + case('weno5') + allocate(weno5_reconstructor :: recon) + select type(recon) + type is(weno5_reconstructor) + recon%order = order + end select + + case default + error_code = CM_ERROR_UNKNOWN_SCHEME + if (config%verbose) then + print *, "[ERROR] Unknown reconstructor scheme: ", scheme + print *, " Available: eno, weno3, weno5" + end if + end select + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Reconstructor creation failed" + end if + end function create_reconstructor + + ! ==================== 通量计算器创建 ==================== + + function create_flux_calculator(config, status) result(flux) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(flux_calculator_base), allocatable :: flux + + character(len=20) :: flux_type + integer :: error_code + + flux_type = trim(adjustl(config%flux_type)) + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating flux calculator: ", flux_type + end if + + select case(flux_type) + case('rusanov') + allocate(rusanov_flux :: flux) + + case default + error_code = CM_ERROR_UNKNOWN_FLUX + if (config%verbose) then + print *, "[ERROR] Unknown flux type: ", flux_type + print *, " Available: rusanov" + end if + end select + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Flux calculator creation failed" + end if + end function create_flux_calculator + +end module component_factory_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/manager/component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/manager/component_manager.f90 new file mode 100644 index 00000000..70d00991 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/manager/component_manager.f90 @@ -0,0 +1,48 @@ +! src/manager/component_manager.f90 +module component_manager_module + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config + + implicit none + private + public :: wp, component_manager_info, validate_config + + ! 定义wp以保持兼容性 + integer, parameter :: wp = real64 + +contains + + ! ==================== 配置验证 ==================== + + function validate_config(config) result(is_valid) + type(cfd_config), intent(in) :: config + logical :: is_valid + + ! 简单验证 + is_valid = .true. + + if (config%verbose) then + print *, "[CONFIG VALIDATION] Configuration is valid (simplified)" + end if + end function validate_config + + ! ==================== 信息显示 ==================== + + subroutine component_manager_info() + print *, "=== Component Manager ===" + print *, "Available reconstructors:" + print *, " - eno (orders: 1-7)" + print *, " - weno3 (order: 3)" + print *, " - weno5 (order: 5)" + print *, "" + print *, "Available flux calculators:" + print *, " - rusanov" + print *, "" + print *, "Features:" + print *, " - Configuration validation" + print *, " - Component creation from config" + print *, " - Error handling with status codes" + print *, "=========================" + end subroutine component_manager_info + +end module component_manager_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..66874a7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/CMakeLists.txt @@ -0,0 +1,7 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..3fde7746 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,28 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux STATIC + base.f90 + rusanov.f90 +) + +#target_link_libraries(flux PRIVATE core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 明确设置不使用 /Qm64 选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + set_target_properties(flux PROPERTIES + Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /Qm64" # 明确设置,避免继承问题 + ) +endif() + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/flux/base.f90 new file mode 100644 index 00000000..7080a7ae --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/flux/base.f90 @@ -0,0 +1,30 @@ +!src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + procedure :: print_basic_info => flux_print_basic ! 添加辅助方法 + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + + subroutine flux_print_basic(this) + class(flux_calculator_base), intent(in) :: this + print *, " Name: ", trim(this%name) + end subroutine flux_print_basic + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/flux/engquist_osher.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/flux/engquist_osher.f90 new file mode 100644 index 00000000..90f499d1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/flux/engquist_osher.f90 @@ -0,0 +1,41 @@ +! src/numerics/flux/engquist_osher.f90 +module engquist_osher_flux + use base_modules, only: wp, ip + use flux_base_module, only: flux_calculator_base + implicit none + private + + type, extends(flux_calculator_base), public :: engquist_osher_flux_t + real(wp) :: wave_speed = 1.0_wp + contains + procedure :: compute => eo_compute + end type + +contains + + subroutine eo_compute(this, qL, qR, flux, equation) + class(engquist_osher_flux_t), intent(inout) :: this + real(wp), intent(in) :: qL(:), qR(:) + real(wp), intent(out) :: flux(:) + class(*), intent(in) :: equation + + integer :: i, n + real(wp) :: cp, cm, uL, uR + + select type(eq => equation) + type is (linear_advection_eq) + cp = 0.5_wp * (eq%wave_speed + abs(eq%wave_speed)) + cm = 0.5_wp * (eq%wave_speed - abs(eq%wave_speed)) + + n = size(qL) + do i = 1, n + uL = qL(i) + uR = qR(i) + flux(i) = cp * uL + cm * uR + end do + class default + error stop "Unsupported equation type for Engquist-Osher flux" + end select + end subroutine + +end module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..daa9e3bb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/flux/rusanov.f90 @@ -0,0 +1,41 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux, create_rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + + ! 构造函数接口 + interface rusanov_flux + module procedure create_rusanov_flux + end interface + +contains + + ! 构造函数 + type(rusanov_flux) function create_rusanov_flux() result(this) + this%name = "Rusanov" + this%wave_speed_default = 1.0_real64 + end function create_rusanov_flux + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Flux calculator information:" + call this%print_basic_info() + + ! 添加Rusanov特有信息 + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..c88ea647 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,23 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor STATIC + base.f90 + eno.f90 + weno3.f90 + weno5.f90 +) + +target_link_libraries(reconstructor PRIVATE core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..53798d02 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/reconstructor/base.f90 @@ -0,0 +1,33 @@ +!src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + procedure :: print_basic_info => reconstructor_print_basic ! 添加一个辅助方法 + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + + subroutine reconstructor_print_basic(this) + class(reconstructor_base), intent(in) :: this + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_print_basic + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..f973e8b3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor, create_eno_reconstructor ! ← 添加这个 + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + + ! 构造函数接口 + interface eno_reconstructor + module procedure create_eno_reconstructor + end interface + +contains + + ! 构造函数 + type(eno_reconstructor) function create_eno_reconstructor() result(this) + this%name = "ENO" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_eno_reconstructor + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加ENO特有信息 + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..a8faa577 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,40 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + private + + type, extends(reconstructor_base), public :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + + ! 构造函数接口 - 使用类型名本身作为构造函数 + interface weno3_reconstructor + module procedure create_weno3_reconstructor + end interface + +contains + + ! 构造函数 + type(weno3_reconstructor) function create_weno3_reconstructor() result(this) + this%name = "WENO3" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_weno3_reconstructor + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO3特有信息 + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/reconstructor/weno5.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/reconstructor/weno5.f90 new file mode 100644 index 00000000..a869c67d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/numerics/reconstructor/weno5.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/weno5.f90(新增) +module weno5_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno5_reconstructor, create_weno5_reconstructor + + type, extends(reconstructor_base) :: weno5_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno5_info + end type weno5_reconstructor + + ! 构造函数接口 + interface weno5_reconstructor + module procedure create_weno5_reconstructor + end interface + +contains + + ! 构造函数 + type(weno5_reconstructor) function create_weno5_reconstructor() result(this) + this%name = "WENO5" + this%order = 5 + this%epsilon = 1.0e-6_real64 + end function create_weno5_reconstructor + + subroutine weno5_info(this) + class(weno5_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO5特有信息 + print *, " Type: WENO-5 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno5_info + +end module weno5_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/physics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03f/src/physics/CMakeLists.txt new file mode 100644 index 00000000..cc4e233a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/physics/CMakeLists.txt @@ -0,0 +1,19 @@ +# src/physics/CMakeLists.txt +message(STATUS "配置物理模块...") + +# 创建物理模块库 +add_library(physics STATIC + physics_interface.f90 + equations/linear_convection.f90 + problems/linear_convection_problem.f90 +) + +# 链接依赖 +target_link_libraries(physics PRIVATE base) + +# 设置模块输出目录 +set_target_properties(physics PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "物理模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/physics/equations/linear_convection.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/physics/equations/linear_convection.f90 new file mode 100644 index 00000000..3be3b8f7 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/physics/equations/linear_convection.f90 @@ -0,0 +1,43 @@ +! src/physics/equations/linear_advection.f90 +module linear_advection_equation + use base_modules, only: wp, ip + implicit none + private + + type, public :: linear_advection_eq + real(wp) :: wave_speed = 1.0_wp + contains + procedure :: flux => eq_flux + procedure :: max_wave_speed => eq_max_wave_speed + procedure :: num_eqs => eq_num_eqs + procedure :: print_info => eq_print_info + end type + +contains + + pure function eq_flux(this, u) result(f) + class(linear_advection_eq), intent(in) :: this + real(wp), intent(in) :: u + real(wp) :: f + f = this%wave_speed * u + end function + + pure function eq_max_wave_speed(this, u) result(smax) + class(linear_advection_eq), intent(in) :: this + real(wp), intent(in) :: u + real(wp) :: smax + smax = abs(this%wave_speed) + end function + + integer function eq_num_eqs(this) + class(linear_advection_eq), intent(in) :: this + eq_num_eqs = 1 + end function + + subroutine eq_print_info(this) + class(linear_advection_eq), intent(in) :: this + print *, "Linear Advection Equation:" + print *, " Wave speed: ", this%wave_speed + end subroutine + +end module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/physics/physics_interface.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/physics/physics_interface.f90 new file mode 100644 index 00000000..45002da6 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/physics/physics_interface.f90 @@ -0,0 +1,64 @@ +! src/physics/physics_interface.f90 +module physics_interface + use precision_module, only: wp, ip + implicit none + private + + ! 定义抽象基类型 - 先声明为私有,然后在公开部分导出 + type, abstract :: physics_equation + character(len=:), allocatable :: name + contains + procedure(eq_flux_abs), deferred :: flux + procedure(eq_speed_abs), deferred :: speed + end type physics_equation + + type, abstract :: physics_problem + character(len=:), allocatable :: name + contains + procedure(prob_ic_abs), deferred :: initial_condition + procedure(prob_bc_abs), deferred :: boundary_condition + procedure(prob_exact_abs), deferred :: exact_solution + end type physics_problem + + ! 抽象接口定义 + abstract interface + pure function eq_flux_abs(this, u) result(f) + import :: physics_equation, wp + class(physics_equation), intent(in) :: this + real(wp), intent(in) :: u + real(wp) :: f + end function eq_flux_abs + + pure function eq_speed_abs(this) result(a) + import :: physics_equation, wp + class(physics_equation), intent(in) :: this + real(wp) :: a + end function eq_speed_abs + + subroutine prob_ic_abs(this, x, u) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp), intent(out) :: u(:) + end subroutine prob_ic_abs + + subroutine prob_bc_abs(this, u, t) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(inout) :: u(:) + real(wp), intent(in), optional :: t + end subroutine prob_bc_abs + + function prob_exact_abs(this, x, t) result(u) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(in) :: x(:), t + real(wp), dimension(size(x)) :: u + end function prob_exact_abs + end interface + + ! 公开接口 - 使用独立的public语句 + public :: wp, ip + public :: physics_equation, physics_problem + +end module physics_interface \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/physics/problems/linear_advection_problem.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/physics/problems/linear_advection_problem.f90 new file mode 100644 index 00000000..419d57cf --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/physics/problems/linear_advection_problem.f90 @@ -0,0 +1,70 @@ +! src/physics/problems/linear_advection_problem.f90 +module linear_advection_problem + use base_modules, only: wp, ip + use initial_condition_module, only: create_initial_condition + use boundary_condition_module, only: create_boundary_condition + implicit none + private + + type, public :: linear_advection_problem + character(len=20) :: ic_type = "step" + character(len=20) :: boundary_type = "periodic" + real(wp) :: left_value = 1.0_wp + real(wp) :: right_value = 2.0_wp + real(wp) :: domain_length = 2.0_wp + real(wp) :: wave_speed = 1.0_wp + contains + procedure :: create_ic => prob_create_ic + procedure :: create_bc => prob_create_bc + procedure :: exact_solution => prob_exact_solution + end type + +contains + + function prob_create_ic(this, config) result(ic) + class(linear_advection_problem), intent(in) :: this + class(*), intent(in) :: config + class(*), allocatable :: ic + + ! 通过初始条件工厂创建 + call create_initial_condition(this%ic_type, config, ic) + end function + + function prob_create_bc(this, cfd) result(bc) + class(linear_advection_problem), intent(in) :: this + class(*), intent(in) :: cfd + class(*), allocatable :: bc + + ! 通过边界条件工厂创建 + call create_boundary_condition(this%boundary_type, cfd, bc) + end function + + function prob_exact_solution(this, x, t) result(u) + class(linear_advection_problem), intent(in) :: this + real(wp), intent(in) :: x(:), t + real(wp), allocatable :: u(:) + + integer :: i, n + real(wp) :: x_shifted, L + + n = size(x) + L = this%domain_length + allocate(u(n)) + + ! 周期性平移 + do i = 1, n + x_shifted = x(i) - this%wave_speed * t + x_shifted = modulo(x_shifted, L) + if (x_shifted < 0.0_wp) x_shifted = x_shifted + L + + ! 初始条件逻辑(简化) + if (this%ic_type == "step" .and. & + x_shifted >= 0.5_wp .and. x_shifted <= 1.0_wp) then + u(i) = 2.0_wp + else + u(i) = 1.0_wp + end if + end do + end function + +end module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/physics/problems/linear_convection_problem.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/physics/problems/linear_convection_problem.f90 new file mode 100644 index 00000000..06226ed1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/physics/problems/linear_convection_problem.f90 @@ -0,0 +1,118 @@ +! src/physics/problems/linear_convection_problem.f90 +module linear_convection_problem + use precision_module, only: wp, ip + use physics_interface, only: physics_problem + implicit none + private + + ! 具体问题类型 - 先声明 + type, extends(physics_problem) :: linear_convection_prob + real(wp) :: wave_speed = 1.0_wp + real(wp) :: domain_length = 2.0_wp + character(len=20) :: ic_type = "step" + character(len=20) :: boundary_type = "periodic" + contains + procedure :: initial_condition => lc_initial_condition + procedure :: boundary_condition => lc_boundary_condition + procedure :: exact_solution => lc_exact_solution + end type linear_convection_prob + + ! 公开接口 + public :: wp, ip + public :: linear_convection_prob, create_linear_convection_prob + +contains + + ! 构造函数 + function create_linear_convection_prob(wave_speed, domain_length, & + ic_type, boundary_type) result(prob) + real(wp), intent(in), optional :: wave_speed, domain_length + character(len=*), intent(in), optional :: ic_type, boundary_type + type(linear_convection_prob) :: prob + + prob%name = "Linear Convection Problem" + + if (present(wave_speed)) prob%wave_speed = wave_speed + if (present(domain_length)) prob%domain_length = domain_length + if (present(ic_type)) prob%ic_type = ic_type + if (present(boundary_type)) prob%boundary_type = boundary_type + end function create_linear_convection_prob + + ! 初始条件 + subroutine lc_initial_condition(this, x, u) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp), intent(out) :: u(:) + + integer :: i + + select case (trim(this%ic_type)) + case ("step") + do i = 1, size(x) + if (x(i) >= 0.5_wp .and. x(i) <= 1.0_wp) then + u(i) = 2.0_wp + else + u(i) = 1.0_wp + end if + end do + + case ("sin", "sine") + do i = 1, size(x) + u(i) = sin(2.0_wp * 3.141592653589793_wp * x(i) / this%domain_length) + end do + + case ("gaussian") + do i = 1, size(x) + u(i) = exp(-((x(i) - 0.5_wp) / 0.1_wp)**2) + end do + + case default + ! 默认阶跃函数 + do i = 1, size(x) + if (x(i) >= 0.5_wp .and. x(i) <= 1.0_wp) then + u(i) = 2.0_wp + else + u(i) = 1.0_wp + end if + end do + end select + end subroutine lc_initial_condition + + ! 边界条件(虚拟实现,实际在boundary模块) + subroutine lc_boundary_condition(this, u, t) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(inout) :: u(:) + real(wp), intent(in), optional :: t + + ! 边界条件将在独立模块实现 + print *, "[PROBLEM] Boundary condition placeholder" + if (present(t)) then + print *, " Time = ", t + end if + end subroutine lc_boundary_condition + + ! 精确解(周期性平移) + function lc_exact_solution(this, x, t) result(u) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(in) :: x(:), t + real(wp), dimension(size(x)) :: u + real(wp), dimension(size(x)) :: x_shifted + integer :: i + + ! 周期性平移 + do i = 1, size(x) + x_shifted(i) = x(i) - this%wave_speed * t + ! 确保在 [0, domain_length) 范围内 + do while (x_shifted(i) < 0.0_wp) + x_shifted(i) = x_shifted(i) + this%domain_length + end do + do while (x_shifted(i) >= this%domain_length) + x_shifted(i) = x_shifted(i) - this%domain_length + end do + end do + + ! 重用初始条件函数 + call this%initial_condition(x_shifted, u) + end function lc_exact_solution + +end module linear_convection_problem \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/results.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/results.f90 new file mode 100644 index 00000000..f7ce0a7a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/results.f90 @@ -0,0 +1,290 @@ +! src/results.f90 (修正版) +module results_module + use base_modules, only: wp, ip + use config_module, only: cfd_config + use mesh_module, only: mesh_type + use domain_module, only: domain_type + use solution_module, only: solution_type + ! use physics_solver_module, only: physics_solver ! 暂时注释掉,避免循环依赖 + + implicit none + private + public :: results_saver, results_saver_create, save_results + + ! 定义字符串长度常量 + integer, parameter :: STR_LEN = 128 + + ! 结果类型 - 对应Julia的result字典 + type :: cfd_results + character(len=STR_LEN) :: solver_name = "" + real(wp), allocatable :: x(:) ! 网格坐标(单元中心) + real(wp), allocatable :: numerical(:) ! 数值解 + real(wp), allocatable :: analytical(:) ! 解析解 + character(len=STR_LEN) :: scheme = "" ! 格式名称 + integer :: order = 0 ! 阶数 + integer :: rk_order = 0 ! RK阶数 + real(wp) :: final_time = 0.0_wp ! 最终时间 + real(wp) :: current_time = 0.0_wp ! 当前时间 + integer :: total_steps = 0 ! 总步数 + integer :: solver_state = 0 ! 求解器状态 + end type cfd_results + + ! 结果保存器 + type :: results_saver + character(len=STR_LEN) :: base_filename = "results" + logical :: verbose = .true. + contains + procedure :: save_text => results_saver_save_text + procedure :: save_binary => results_saver_save_binary + procedure :: load => results_saver_load + end type results_saver + + ! 接口声明 + interface results_saver + module procedure results_saver_constructor + end interface + +contains + + ! 构造函数 + function results_saver_constructor(base_filename, verbose) result(saver) + character(len=*), optional :: base_filename + logical, optional :: verbose + type(results_saver) :: saver + + if (present(base_filename)) then + saver%base_filename = trim(adjustl(base_filename)) + end if + if (present(verbose)) then + saver%verbose = verbose + end if + end function results_saver_constructor + + ! 保持向后兼容的创建函数 + function results_saver_create(base_filename, verbose) result(saver) + character(len=*), optional :: base_filename + logical, optional :: verbose + type(results_saver) :: saver + + saver = results_saver_constructor(base_filename, verbose) + end function results_saver_create + + ! 生成文件名(与Julia风格一致) + function generate_filename(saver, solver_name, mesh_size) result(filename) + class(results_saver), intent(in) :: saver + character(len=*), intent(in) :: solver_name + integer, intent(in) :: mesh_size + character(len=STR_LEN) :: filename + + write(filename, '(A, "_", A, "_", I0, ".dat")') & + trim(saver%base_filename), trim(solver_name), mesh_size + end function generate_filename + + ! 主保存函数 - 生成与Julia兼容的结果 + subroutine save_results(saver, solver_name, config, mesh, domain, solution, & + current_time, total_steps, solver_state) + class(results_saver), intent(in) :: saver + character(len=*), intent(in) :: solver_name + type(cfd_config), intent(in) :: config + type(mesh_type), intent(in) :: mesh + type(domain_type), intent(in) :: domain + type(solution_type), intent(in) :: solution + real(wp), intent(in) :: current_time + integer, intent(in) :: total_steps, solver_state + + type(cfd_results) :: results + character(len=STR_LEN) :: filename + integer :: i, n_physical + + ! 准备结果数据 + results%solver_name = trim(solver_name) + results%scheme = trim(config%recon_scheme) + results%order = config%spatial_order + results%rk_order = config%rk_order + results%final_time = config%final_time + results%current_time = current_time + results%total_steps = total_steps + results%solver_state = solver_state + + ! 分配数组 + n_physical = mesh%ncells + allocate(results%x(n_physical)) + allocate(results%numerical(n_physical)) + allocate(results%analytical(n_physical)) + + ! 填充网格坐标(单元中心) + results%x = mesh%xcc + + ! 填充数值解(仅物理区域) + do i = 1, n_physical + results%numerical(i) = solution%u(domain%ist + i - 1) + end do + + ! 生成解析解(与Julia的exact_solution对应) + call generate_analytical_solution(results%x, config, results%analytical, current_time) + + ! 生成文件名 + filename = generate_filename(saver, solver_name, mesh%ncells) + + ! 保存文件 + if (saver%verbose) then + print *, "[RESULTS] Saving results to: ", trim(filename) + print *, " Solver: ", trim(results%solver_name) + print *, " Scheme: ", trim(results%scheme), " order ", results%order + print *, " Time: ", results%current_time, " / ", results%final_time + print *, " Steps: ", results%total_steps + end if + + call saver%save_text(results, filename) + + ! 清理 + deallocate(results%x, results%numerical, results%analytical) + end subroutine save_results + + ! 生成解析解(匹配Julia的exact_solution逻辑) + subroutine generate_analytical_solution(x, config, analytical, current_time) + real(wp), intent(in) :: x(:), current_time + type(cfd_config), intent(in) :: config + real(wp), intent(out) :: analytical(:) + + integer :: i, n + real(wp) :: x_shifted, L + + n = size(x) + L = config%domain_length + + select case (trim(config%ic_type)) + case ("step") + ! 阶跃函数的精确解(周期性) + do i = 1, n + ! 周期性平移 + x_shifted = x(i) - config%wave_speed * current_time + x_shifted = modulo(x_shifted, L) + if (x_shifted < 0.0_wp) x_shifted = x_shifted + L + + ! 阶跃在 [0.5, 1.0] 内为 2.0,其他为 1.0 + if (x_shifted >= 0.5_wp .and. x_shifted <= 1.0_wp) then + analytical(i) = 2.0_wp + else + analytical(i) = 1.0_wp + end if + end do + + case ("sin", "sine") + ! 正弦波的精确解 + do i = 1, n + x_shifted = x(i) - config%wave_speed * current_time + x_shifted = modulo(x_shifted, L) + analytical(i) = sin(2.0_wp * 3.141592653589793_wp * x_shifted / L) + end do + + case ("gaussian") + ! 高斯脉冲的精确解 + do i = 1, n + x_shifted = x(i) - config%wave_speed * current_time + x_shifted = modulo(x_shifted, L) + analytical(i) = exp(-50.0_wp * (x_shifted - 1.0_wp)**2) + end do + + case default + ! 默认:阶跃函数 + do i = 1, n + x_shifted = x(i) - config%wave_speed * current_time + x_shifted = modulo(x_shifted, L) + if (x_shifted >= 0.5_wp .and. x_shifted <= 1.0_wp) then + analytical(i) = 2.0_wp + else + analytical(i) = 1.0_wp + end if + end do + end select + end subroutine generate_analytical_solution + + ! 文本格式保存(与Julia的纯文本输出兼容) + subroutine results_saver_save_text(this, results, filename) + class(results_saver), intent(in) :: this + type(cfd_results), intent(in) :: results + character(len=*), intent(in) :: filename + + integer :: i, n, unit, ierr + + n = size(results%x) + + ! 打开文件 + open(newunit=unit, file=trim(filename), status='replace', & + action='write', iostat=ierr) + + if (ierr /= 0) then + if (this%verbose) then + print *, "[ERROR] Cannot open file: ", trim(filename) + end if + return + end if + + ! 写入头部信息(类似Julia的输出格式) + write(unit, '(A)') "========================================" + write(unit, '(A)') "CFD SOLVER RESULTS (Fortran)" + write(unit, '(A)') "========================================" + write(unit, '(A, A)') "Solver: ", trim(results%solver_name) + write(unit, '(A, A)') "Scheme: ", trim(results%scheme) + write(unit, '(A, I0)') "Order: ", results%order + write(unit, '(A, I0)') "RK Order: ", results%rk_order + write(unit, '(A, ES15.8)') "Final Time: ", results%final_time + write(unit, '(A, ES15.8)') "Current Time: ", results%current_time + write(unit, '(A, I0)') "Total Steps: ", results%total_steps + write(unit, '(A, I0)') "Solver State: ", results%solver_state + write(unit, '(A, I0)') "Grid Points: ", n + write(unit, '(A)') "========================================" + write(unit, '(A)') "DATA: x, numerical, analytical" + write(unit, '(A)') "========================================" + + ! 写入数据 + do i = 1, n + write(unit, '(3ES20.12)') results%x(i), results%numerical(i), results%analytical(i) + end do + + ! 关闭文件 + close(unit) + + if (this%verbose) then + print *, "[RESULTS] Saved ", n, " data points to ", trim(filename) + end if + end subroutine results_saver_save_text + + ! 二进制保存(可选) + subroutine results_saver_save_binary(this, results, filename) + class(results_saver), intent(in) :: this + type(cfd_results), intent(in) :: results + character(len=*), intent(in) :: filename + + ! 暂时实现文本格式,二进制格式可后续添加 + if (this%verbose) then + print *, "[INFO] Binary save not implemented, using text format" + end if + call this%save_text(results, filename) + end subroutine results_saver_save_binary + + ! 加载结果(暂时简单实现) + subroutine results_saver_load(this, filename, results) + class(results_saver), intent(in) :: this + character(len=*), intent(in) :: filename + type(cfd_results), intent(out) :: results + + ! 简化:只打印文件信息 + if (this%verbose) then + print *, "[RESULTS] Would load from: ", trim(filename) + print *, " Note: Load functionality needs implementation" + end if + + ! 初始化结果结构以避免未初始化警告 + results%solver_name = "" + results%scheme = "" + results%order = 0 + results%rk_order = 0 + results%final_time = 0.0_wp + results%current_time = 0.0_wp + results%total_steps = 0 + results%solver_state = 0 + end subroutine results_saver_load + +end module results_module diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/solver/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03f/src/solver/CMakeLists.txt new file mode 100644 index 00000000..a8821242 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/solver/CMakeLists.txt @@ -0,0 +1,44 @@ +# src/solver/CMakeLists.txt(统一管理所有求解器) +message(STATUS "配置求解器模块...") + +# 基础求解器库 +add_library(solver STATIC + base.f90 + physics_solver.f90 +) + +target_link_libraries(solver + PRIVATE + infrastructure + core + physics + manager + results + boundary # 添加依赖 + initial_condition # 添加依赖 +) + +set_target_properties(solver PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 集成求解器库(在同一个目录下) +add_library(solver_integrated STATIC + solver_integrated.f90 +) + +target_link_libraries(solver_integrated + PRIVATE + solver # 基础求解器 + infrastructure + base + boundary + initial_condition + results # 如果需要保存结果 +) + +set_target_properties(solver_integrated PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "求解器模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/solver/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/solver/base.f90 new file mode 100644 index 00000000..cfd78c47 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/solver/base.f90 @@ -0,0 +1,264 @@ +! src/solver/base.f90 +module solver_base_module + use base_modules, only: wp => wp, ip => ip ! 重命名以避免冲突 + + use config_module, only: cfd_config + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create + + implicit none + private + + ! 明确导出列表 + public :: wp, ip ! 类型参数 + public :: solver_base, create_solver_base ! 类型和构造函数 + public :: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, SOLVER_RUNNING + public :: SOLVER_COMPLETED, SOLVER_ERROR ! 状态常量 + + ! 求解器状态枚举 + integer, parameter :: SOLVER_UNINITIALIZED = 0 + integer, parameter :: SOLVER_INITIALIZED = 1 + integer, parameter :: SOLVER_RUNNING = 2 + integer, parameter :: SOLVER_COMPLETED = 3 + integer, parameter :: SOLVER_ERROR = 4 + + ! 求解器基类 + type :: solver_base + ! 基本组件 + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + + ! 状态管理 + integer :: state = SOLVER_UNINITIALIZED + character(len=100) :: error_message = "" + real(wp) :: current_time = 0.0_wp + integer(ip) :: current_step = 0 + + ! 时间控制 + real(wp) :: dt_original = 0.0_wp + contains + procedure :: initialize => solver_base_initialize + procedure :: step => solver_base_step + procedure :: run_to_time => solver_base_run_to_time + procedure :: cleanup => solver_base_cleanup + procedure :: get_state => solver_base_get_state + procedure :: get_error => solver_base_get_error + procedure :: print_info => solver_base_print_info + end type solver_base + + ! 构造函数接口 + interface solver_base + module procedure create_solver_base + end interface + +contains + + ! ==================== 构造函数 ==================== + + function create_solver_base(config, mesh) result(solver) + type(cfd_config), intent(in) :: config + type(mesh_type), intent(in) :: mesh + type(solver_base) :: solver + + solver%config = config + solver%mesh = mesh + + ! 创建域 + solver%domain = domain_create(config, mesh) + + ! 创建解 + solver%solution = solution_create(solver%domain) + + ! 保存原始时间步长 + solver%dt_original = config%dt + + if (config%verbose) then + print *, "[SOLVER] Base solver created" + print *, " Mesh cells: ", mesh%ncells + print *, " Domain total cells: ", solver%domain%ntcells + end if + end function create_solver_base + + ! ==================== 初始化 ==================== + + subroutine solver_base_initialize(this) + class(solver_base), intent(inout) :: this + + if (this%state == SOLVER_INITIALIZED) then + if (this%config%verbose) then + print *, "[SOLVER] Already initialized" + end if + return + end if + + ! 初始化解(通过配置) + ! 这里暂时简化,实际需要调用初始条件工厂 + print *, "[INFO] Base solver initialized (simplified)" + + ! 更新状态 + this%state = SOLVER_INITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + + if (this%config%verbose) then + print *, "[SOLVER] Initialized at t = ", this%current_time + end if + end subroutine solver_base_initialize + + ! ==================== 单步计算(虚方法) ==================== + + subroutine solver_base_step(this, dt) + class(solver_base), intent(inout) :: this + real(wp), intent(in) :: dt + + ! 基类中这只是虚方法,需要在子类中实现 + print *, "[INFO] Base solver step (virtual method)" + print *, " dt = ", dt + print *, " t = ", this%current_time + + ! 更新时间 + this%current_time = this%current_time + dt + this%current_step = this%current_step + 1 + + ! 简单模拟:只是更新状态 + if (this%config%verbose) then + print *, "[SOLVER] Step completed: t = ", this%current_time, & + ", step = ", this%current_step + end if + end subroutine solver_base_step + + ! ==================== 运行到指定时间 ==================== + + subroutine solver_base_run_to_time(this, final_time) + class(solver_base), intent(inout) :: this + real(wp), intent(in) :: final_time + + real(wp) :: dt, t_remaining + integer :: step_count + + if (this%state /= SOLVER_INITIALIZED) then + this%error_message = "Solver not initialized" + this%state = SOLVER_ERROR + if (this%config%verbose) then + print *, "[SOLVER BASE ERROR] Not initialized: ", trim(this%error_message) + end if + return + end if + + this%state = SOLVER_RUNNING + step_count = 0 + + if (this%config%verbose) then + print *, "[SOLVER BASE] Running from t = ", this%current_time, & + " to t = ", final_time + print *, " Time step: ", this%config%dt + end if + + do while (this%current_time < final_time - 1e-12_wp) + ! 计算时间步长 + t_remaining = final_time - this%current_time + dt = min(this%config%dt, t_remaining) + + ! 执行时间步 + call this%step(dt) + + step_count = step_count + 1 + + ! 每50步输出一次进度 + if (mod(step_count, 50) == 0 .and. this%config%verbose) then + print *, "[SOLVER BASE] Progress: t = ", this%current_time, & + " / ", final_time, " (step ", step_count, ")" + end if + end do + + ! 恢复原始时间步长 + this%config%dt = this%dt_original + + ! 更新状态 + this%state = SOLVER_COMPLETED + + if (this%config%verbose) then + print *, "[SOLVER BASE] Run completed:" + print *, " Final time: ", this%current_time + print *, " Total steps: ", this%current_step + print *, " State: ", this%state + end if + end subroutine solver_base_run_to_time + + ! ==================== 清理 ==================== + + subroutine solver_base_cleanup(this) + class(solver_base), intent(inout) :: this + + ! 重置状态 + this%state = SOLVER_UNINITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + this%error_message = "" + + if (this%config%verbose) then + print *, "[SOLVER] Cleaned up" + end if + end subroutine solver_base_cleanup + + ! ==================== 状态查询 ==================== + + function solver_base_get_state(this) result(state) + class(solver_base), intent(in) :: this + integer :: state + state = this%state + end function solver_base_get_state + + function solver_base_get_error(this) result(error_msg) + class(solver_base), intent(in) :: this + character(len=100) :: error_msg + error_msg = trim(this%error_message) + end function solver_base_get_error + + ! ==================== 信息打印 ==================== + + subroutine solver_base_print_info(this) + class(solver_base), intent(in) :: this + + character(len=20) :: state_str + + ! 状态字符串 + select case (this%state) + case (SOLVER_UNINITIALIZED) + state_str = "Uninitialized" + case (SOLVER_INITIALIZED) + state_str = "Initialized" + case (SOLVER_RUNNING) + state_str = "Running" + case (SOLVER_COMPLETED) + state_str = "Completed" + case (SOLVER_ERROR) + state_str = "Error" + case default + state_str = "Unknown" + end select + + print *, "=== Solver Information ===" + print *, "State: ", trim(state_str) + print *, "Current time: ", this%current_time + print *, "Current step: ", this%current_step + print *, "Error message: '", trim(this%error_message), "'" + + ! 配置信息 + print *, "Configuration:" + print *, " Scheme: ", trim(this%config%recon_scheme) + print *, " Order: ", this%config%spatial_order + print *, " dt: ", this%config%dt + + ! 域信息 + print *, "Domain:" + print *, " Ghost layers: ", this%domain%nghosts + print *, " Physical cells: ", this%domain%ist, " to ", this%domain%ied - 1 + + print *, "=========================" + end subroutine solver_base_print_info + +end module solver_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/solver/physics_solver.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/solver/physics_solver.f90 new file mode 100644 index 00000000..4582042d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/solver/physics_solver.f90 @@ -0,0 +1,188 @@ +! src/solver/physics_solver.f90 (修正版) +module physics_solver_module + use base_modules, only: wp => wp, ip => ip + use config_module, only: cfd_config + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create + + implicit none + private + + ! 明确导出列表 + public :: wp, ip, physics_solver + public :: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, SOLVER_COMPLETED, SOLVER_ERROR + + ! 求解器状态枚举 + integer, parameter :: SOLVER_UNINITIALIZED = 0 + integer, parameter :: SOLVER_INITIALIZED = 1 + integer, parameter :: SOLVER_RUNNING = 2 + integer, parameter :: SOLVER_COMPLETED = 3 + integer, parameter :: SOLVER_ERROR = 4 + + ! 物理求解器类型 + type :: physics_solver + ! 基本组件 + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + + ! 状态管理 + integer :: state = SOLVER_UNINITIALIZED + character(len=100) :: error_message = "" + real(wp) :: current_time = 0.0_wp + integer(ip) :: current_step = 0 + + ! 时间控制 + real(wp) :: dt_original = 0.0_wp + contains + procedure :: initialize => physics_solver_initialize + procedure :: run_to_time => physics_solver_run_to_time + procedure :: cleanup => physics_solver_cleanup + procedure :: get_state => physics_solver_get_state + procedure, private :: apply_simple_initial_condition ! 添加这行 + end type physics_solver + +contains + + ! ==================== 初始化 ==================== + + subroutine physics_solver_initialize(this) + class(physics_solver), intent(inout) :: this + + if (this%state == SOLVER_INITIALIZED) then + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Already initialized" + end if + return + end if + + ! 创建域 + this%domain = domain_create(this%config, this%mesh) + + ! 创建解 + this%solution = solution_create(this%domain) + + ! 应用简化的初始条件 + call this%apply_simple_initial_condition() + + ! 保存原始时间步长 + this%dt_original = this%config%dt + + ! 更新状态 + this%state = SOLVER_INITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Initialized at t = ", this%current_time + end if + end subroutine physics_solver_initialize + + subroutine apply_simple_initial_condition(this) + class(physics_solver), intent(inout) :: this + + integer :: i, idx + real(wp) :: x + + ! 简化的阶跃函数初始条件 + do i = this%domain%ist, this%domain%ied - 1 + idx = i - this%domain%ist + 1 + x = this%mesh%xcc(idx) + + if (x >= 0.5_wp .and. x <= 1.0_wp) then + this%solution%u(i) = 2.0_wp + else + this%solution%u(i) = 1.0_wp + end if + end do + + ! 同步旧场 + this%solution%un = this%solution%u + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Applied simple initial condition" + end if + end subroutine + + ! ==================== 运行到指定时间 ==================== + + subroutine physics_solver_run_to_time(this, final_time) + class(physics_solver), intent(inout) :: this + real(wp), intent(in) :: final_time + + real(wp) :: dt, t_remaining + integer :: step_count + + if (this%state /= SOLVER_INITIALIZED) then + this%error_message = "Solver not initialized" + this%state = SOLVER_ERROR + if (this%config%verbose) then + print *, "[PHYSICS SOLVER ERROR] Not initialized" + end if + return + end if + + this%state = SOLVER_RUNNING + step_count = 0 + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Running from t = ", this%current_time, & + " to t = ", final_time + end if + + do while (this%current_time < final_time - 1e-12_wp) + ! 计算时间步长 + t_remaining = final_time - this%current_time + dt = min(this%config%dt, t_remaining) + + ! 简单的时间步进(占位符) + this%current_time = this%current_time + dt + this%current_step = this%current_step + 1 + step_count = step_count + 1 + + ! 每100步输出一次进度 + if (mod(step_count, 100) == 0 .and. this%config%verbose) then + print *, "[PHYSICS SOLVER] Progress: t = ", this%current_time, & + " / ", final_time, " (step ", step_count, ")" + end if + end do + + ! 恢复原始时间步长 + this%config%dt = this%dt_original + + ! 更新状态 + this%state = SOLVER_COMPLETED + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Run completed:" + print *, " Final time: ", this%current_time + print *, " Total steps: ", this%current_step + end if + end subroutine physics_solver_run_to_time + + ! ==================== 清理 ==================== + + subroutine physics_solver_cleanup(this) + class(physics_solver), intent(inout) :: this + + this%state = SOLVER_UNINITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + this%error_message = "" + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Cleaned up" + end if + end subroutine physics_solver_cleanup + + ! ==================== 状态查询 ==================== + + function physics_solver_get_state(this) result(state) + class(physics_solver), intent(in) :: this + integer :: state + state = this%state + end function physics_solver_get_state + +end module physics_solver_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/solver/residual.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/solver/residual.f90 new file mode 100644 index 00000000..91ec2c71 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/solver/residual.f90 @@ -0,0 +1,69 @@ +! src/solver/residual.f90 +module residual_module + use base_modules, only: wp, ip + use domain_module, only: domain_type + use solution_module, only: solution_type + use mesh_module, only: mesh_type + implicit none + private + + type, public :: residual_calculator + class(*), pointer :: reconstructor => null() + class(*), pointer :: flux_calc => null() + class(*), pointer :: equation => null() + real(wp), pointer :: qL(:) => null() + real(wp), pointer :: qR(:) => null() + real(wp), pointer :: flux(:) => null() + real(wp), pointer :: res(:) => null() + real(wp) :: dx = 0.0_wp + contains + procedure :: compute => residual_compute + procedure :: init => residual_init + end type + +contains + + subroutine residual_init(this, reconstructor, flux_calc, equation, & + qL, qR, flux, res, dx) + class(residual_calculator), intent(inout) :: this + class(*), intent(in) :: reconstructor, flux_calc, equation + real(wp), intent(in), target :: qL(:), qR(:), flux(:), res(:) + real(wp), intent(in) :: dx + + this%reconstructor => reconstructor + this%flux_calc => flux_calc + this%equation => equation + this%qL => qL + this%qR => qR + this%flux => flux + this%res => res + this%dx = dx + end subroutine + + subroutine residual_compute(this, u) + class(residual_calculator), intent(inout) :: this + real(wp), intent(in) :: u(:) + + integer :: i, n + real(wp) :: f_left, f_right + + ! 阶段1: 重构界面值 (当前为占位符) + print *, "[RESIDUAL] Reconstructing interface values (placeholder)" + + ! 阶段2: 计算通量 (当前为占位符) + print *, "[RESIDUAL] Computing fluxes (placeholder)" + + ! 阶段3: 计算残差 (真实计算示例) + n = size(this%res) + do i = 1, n + ! 简单的一阶迎风格式作为占位符 + f_left = this%equation%flux(u(i)) + f_right = this%equation%flux(u(i+1)) + this%res(i) = -(f_right - f_left) / this%dx + end do + + print *, "[RESIDUAL] Residual computed, range: ", & + minval(this%res), " to ", maxval(this%res) + end subroutine + +end module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/src/solver/solver_integrated.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/src/solver/solver_integrated.f90 new file mode 100644 index 00000000..6a9a0bfd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/src/solver/solver_integrated.f90 @@ -0,0 +1,390 @@ +! src/solver/solver_integrated.f90 (完全修正版) +module solver_integrated_module + use base_modules, only: wp, ip + use config_module, only: cfd_config + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create + use boundary_base_module, only: boundary_condition + use periodic_boundary_module, only: periodic_boundary + + implicit none + private + + ! 求解器状态常量 + integer, parameter, public :: SOLVER_READY = 0 + integer, parameter, public :: SOLVER_INITIALIZED = 1 + integer, parameter, public :: SOLVER_RUNNING = 2 + integer, parameter, public :: SOLVER_COMPLETED = 3 + integer, parameter, public :: SOLVER_ERROR = -1 + + type, public :: integrated_solver + ! 基本组件 + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + + ! 组件实例 + class(boundary_condition), allocatable :: bc + + ! 状态 + integer :: state = SOLVER_READY + real(wp) :: current_time = 0.0_wp + integer(ip) :: current_step = 0 + character(len=100) :: error_msg = "" + + ! 数据模式 + logical :: use_real_data = .true. + + contains + procedure :: initialize => solver_initialize + procedure :: run_to_time => solver_run_to_time + procedure :: cleanup => solver_cleanup + procedure :: get_state => solver_get_state + procedure :: enable_real_data => solver_enable_real_data + procedure, private :: apply_initial_condition + procedure, private :: apply_boundary_conditions + procedure, private :: simple_time_step + procedure, private :: calculate_dt + procedure, private :: setup_boundary_condition ! 修改方法名 + end type integrated_solver + +contains + + ! ==================== 公共接口 ==================== + + subroutine solver_initialize(this) + class(integrated_solver), intent(inout) :: this + + if (this%state /= SOLVER_READY) then + this%error_msg = "Solver already initialized" + this%state = SOLVER_ERROR + if (this%config%verbose) then + print *, "[ERROR] ", trim(this%error_msg) + end if + return + end if + + ! 创建域 + this%domain = domain_create(this%config, this%mesh) + + ! 创建解 + this%solution = solution_create(this%domain) + + ! 设置边界条件 + call this%setup_boundary_condition() ! 修改调用 + + ! 应用初始条件 + call this%apply_initial_condition() + + ! 初始化状态 + this%state = SOLVER_INITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + + if (this%config%verbose) then + print *, "[INTEGRATED SOLVER] Initialized successfully" + print *, " Domain cells (with ghosts): ", this%domain%ntcells + print *, " Ghost layers: ", this%domain%nghosts + if (this%use_real_data) then + print *, " Data mode: Real" + else + print *, " Data mode: Simple" + end if + end if + end subroutine solver_initialize + + ! ==================== 边界条件设置 ==================== + + subroutine setup_boundary_condition(this) + class(integrated_solver), intent(inout) :: this + + ! 根据配置创建边界条件 + select case (trim(this%config%boundary_type)) + case ("periodic") + ! 创建周期性边界条件 + if (.not. allocated(this%bc)) then + allocate(periodic_boundary :: this%bc) + end if + + select type(bc => this%bc) + type is (periodic_boundary) + bc%name = "periodic" + end select + + case default + ! 默认使用周期性边界 + if (this%config%verbose) then + print *, "[WARNING] Using periodic boundary as default" + end if + + if (.not. allocated(this%bc)) then + allocate(periodic_boundary :: this%bc) + end if + + select type(bc => this%bc) + type is (periodic_boundary) + bc%name = "periodic" + end select + end select + + if (this%config%verbose) then + print *, "[INTEGRATED SOLVER] Boundary condition created: ", & + trim(this%bc%get_name()) + end if + end subroutine setup_boundary_condition + + subroutine solver_run_to_time(this, final_time) + class(integrated_solver), intent(inout) :: this + real(wp), intent(in) :: final_time + + real(wp) :: dt, t_remaining + integer :: step_count + real(wp) :: original_dt + + if (this%state /= SOLVER_INITIALIZED) then + this%error_msg = "Solver not initialized" + this%state = SOLVER_ERROR + if (this%config%verbose) then + print *, "[ERROR] ", trim(this%error_msg) + end if + return + end if + + ! 保存原始时间步长 + original_dt = this%config%dt + + this%state = SOLVER_RUNNING + step_count = 0 + + if (this%config%verbose) then + print *, "[INTEGRATED SOLVER] Starting time integration" + print *, " Initial time: ", this%current_time + print *, " Final time: ", final_time + print *, " Initial dt: ", this%config%dt + end if + + do while (this%current_time < final_time - 1e-12_wp) + ! 计算时间步长 + call this%calculate_dt() + + ! 确保不超过最终时间 + t_remaining = final_time - this%current_time + dt = min(this%config%dt, t_remaining) + + ! 应用边界条件 + call this%apply_boundary_conditions() + + ! 执行时间步 + call this%simple_time_step(dt) + + ! 更新时间 + this%current_time = this%current_time + dt + this%current_step = this%current_step + 1 + step_count = step_count + 1 + + ! 进度输出 + if (mod(step_count, 50) == 0 .and. this%config%verbose) then + print *, " Progress: t = ", this%current_time, & + " / ", final_time, " (step ", step_count, ")" + end if + end do + + ! 恢复原始时间步长 + this%config%dt = original_dt + + ! 更新状态 + this%state = SOLVER_COMPLETED + + if (this%config%verbose) then + print *, "[INTEGRATED SOLVER] Time integration completed" + print *, " Final time: ", this%current_time + print *, " Total steps: ", this%current_step + if (allocated(this%solution%u)) then + print *, " Solution range: [", minval(this%solution%u), ", ", & + maxval(this%solution%u), "]" + end if + end if + end subroutine solver_run_to_time + + subroutine solver_cleanup(this) + class(integrated_solver), intent(inout) :: this + + ! 清理分配的组件 + if (allocated(this%bc)) then + deallocate(this%bc) + end if + + ! 重置状态 + this%state = SOLVER_READY + this%current_time = 0.0_wp + this%current_step = 0 + this%error_msg = "" + + if (this%config%verbose) then + print *, "[INTEGRATED SOLVER] Cleaned up" + end if + end subroutine solver_cleanup + + function solver_get_state(this) result(state) + class(integrated_solver), intent(in) :: this + integer :: state + state = this%state + end function solver_get_state + + subroutine solver_enable_real_data(this, use_real) + class(integrated_solver), intent(inout) :: this + logical, intent(in) :: use_real + this%use_real_data = use_real + + if (this%config%verbose) then + if (use_real) then + print *, "[INTEGRATED SOLVER] Data mode set to: Real" + else + print *, "[INTEGRATED SOLVER] Data mode set to: Simple" + end if + end if + end subroutine solver_enable_real_data + + ! ==================== 私有方法 ==================== + + subroutine apply_initial_condition(this) + class(integrated_solver), intent(inout) :: this + + integer :: i, idx + real(wp) :: x + + if (this%config%verbose) then + print *, "[INTEGRATED SOLVER] Applying initial condition: ", & + trim(this%config%ic_type) + end if + + ! 简化的初始条件应用 + do i = this%domain%ist, this%domain%ied - 1 + idx = i - this%domain%ist + 1 + x = this%mesh%xcc(idx) + + select case (trim(this%config%ic_type)) + case ("step") + ! 阶跃函数 + if (x >= 0.5_wp .and. x <= 1.0_wp) then + this%solution%u(i) = 2.0_wp + else + this%solution%u(i) = 1.0_wp + end if + + case ("sin", "sine") + ! 正弦波 + this%solution%u(i) = sin(2.0_wp * 3.141592653589793_wp * x / & + this%config%domain_length) + + case ("gaussian") + ! 高斯脉冲 + this%solution%u(i) = exp(-((x - 0.5_wp) / 0.1_wp)**2) + + case default + ! 默认阶跃函数 + if (x >= 0.5_wp .and. x <= 1.0_wp) then + this%solution%u(i) = 2.0_wp + else + this%solution%u(i) = 1.0_wp + end if + end select + end do + + ! 同步旧场 + this%solution%un = this%solution%u + + if (this%config%verbose) then + print *, "[INTEGRATED SOLVER] Initial condition applied" + if (allocated(this%solution%u)) then + print *, " Min value: ", minval(this%solution%u) + print *, " Max value: ", maxval(this%solution%u) + end if + end if + end subroutine apply_initial_condition + + subroutine apply_boundary_conditions(this) + class(integrated_solver), intent(inout) :: this + + if (.not. allocated(this%bc)) then + if (this%config%verbose) then + print *, "[WARNING] No boundary condition allocated" + end if + return + end if + + select type(bc => this%bc) + type is (periodic_boundary) + ! 应用周期性边界条件 + call bc%apply(this%solution%u, & + this%domain%nghosts, & + this%domain%ist, & + this%domain%ied - 1) + + class default + ! 对于其他边界条件类型 + if (this%config%verbose) then + print *, "[WARNING] Boundary condition type not fully implemented" + end if + end select + end subroutine apply_boundary_conditions + + subroutine simple_time_step(this, dt) + class(integrated_solver), intent(inout) :: this + real(wp), intent(in) :: dt + + integer :: i + real(wp) :: dx, cfl + + ! 简化的时间步进(一阶迎风格式) + dx = this%mesh%dx + cfl = this%config%wave_speed * dt / dx + + if (cfl > 1.0_wp .and. this%config%verbose) then + print *, "[WARNING] CFL = ", cfl, " > 1.0" + end if + + ! 保存旧解 + this%solution%un = this%solution%u + + ! 一阶迎风格式(只更新内部点) + if (this%domain%ist + 1 <= this%domain%ied - 2) then + do i = this%domain%ist + 1, this%domain%ied - 2 + this%solution%u(i) = this%solution%un(i) - & + cfl * (this%solution%un(i) - this%solution%un(i-1)) + end do + end if + + ! 调试输出 + if (mod(this%current_step, 100) == 0 .and. this%config%verbose) then + print *, " [TIME STEP] Step: ", this%current_step, & + ", t = ", this%current_time, & + ", CFL = ", cfl, & + ", dt = ", dt + end if + end subroutine simple_time_step + + subroutine calculate_dt(this) + class(integrated_solver), intent(inout) :: this + + real(wp) :: cfl, dx + + dx = this%mesh%dx + + if (this%use_real_data) then + ! 真实计算使用CFL条件 + cfl = 0.8_wp ! CFL数 + this%config%dt = cfl * dx / abs(this%config%wave_speed) + else + ! 简单数据使用固定时间步长 + this%config%dt = 0.0025_wp + end if + + if (this%config%verbose .and. this%current_step == 0) then + print *, "[INTEGRATED SOLVER] Calculated dt = ", this%config%dt + end if + end subroutine calculate_dt + +end module solver_integrated_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/CMakeLists.txt new file mode 100644 index 00000000..ced7163e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/CMakeLists.txt @@ -0,0 +1,137 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +# 基础设施测试 +add_executable(test_infrastructure test_infrastructure.f90) +target_link_libraries(test_infrastructure + PRIVATE + infrastructure + core +) + +# 注册系统测试 +add_executable(test_registry test_registry.f90) +target_link_libraries(test_registry + PRIVATE + core + infrastructure +) + +# 物理模块测试 +add_executable(test_physics test_physics.f90) +target_link_libraries(test_physics + PRIVATE + physics + base +) + +# 组件管理器测试 +add_executable(test_component_manager test_component_manager.f90) +target_link_libraries(test_component_manager + PRIVATE + manager + infrastructure +) + +# 配置物理测试 +add_executable(test_config_physics test_config_physics.f90) +target_link_libraries(test_config_physics + PRIVATE + infrastructure + core +) + +# 求解器基础测试 +add_executable(test_solver_base test_solver_base.f90) +target_link_libraries(test_solver_base + PRIVATE + solver + infrastructure + core +) + +# 物理求解器测试 +add_executable(test_physics_solver test_physics_solver.f90) +target_link_libraries(test_physics_solver + PRIVATE + solver + infrastructure + core + physics + manager +) + +# 新增:简单物理求解器测试 +add_executable(test_physics_solver_simple test_physics_solver_simple.f90) +target_link_libraries(test_physics_solver_simple + PRIVATE + solver + infrastructure + core + physics + manager +) + +add_executable(test_simple_link test_simple_link.f90) +target_link_libraries(test_simple_link + PRIVATE + reconstructor + flux +) + + +add_executable(test_factory_simple test_factory_simple.f90) +target_link_libraries(test_factory_simple + PRIVATE + core + infrastructure + reconstructor + flux +) + +add_executable(test_domain_solution test_domain_solution.f90) +target_link_libraries(test_domain_solution + PRIVATE + infrastructure + core +) + +add_executable(test_component_manager_physics test_component_manager_physics.f90) +target_link_libraries(test_component_manager_physics + PRIVATE + manager + infrastructure + physics + core +) + +# 初始条件测试 +add_executable(test_initial_condition test_initial_condition.f90) +target_link_libraries(test_initial_condition + PRIVATE + initial_condition + infrastructure + core + base +) + +# 新增:简单求解器测试(只包含现有的) +# 注释掉缺失文件的测试,或者创建简单的占位符文件 +# add_executable(test_solver_integrated test_solver_integrated.f90) +# target_link_libraries(test_solver_integrated +# PRIVATE +# solver +# infrastructure +# core +# physics +# manager +# ) + +# 注释掉缺失的残差测试 +# add_executable(test_residual test_residual.f90) +# target_link_libraries(test_residual +# PRIVATE +# solver +# infrastructure +# physics +# ) diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_cfd_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_cfd_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_cfd_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_component_manager.f90 new file mode 100644 index 00000000..cf4cf38a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_component_manager.f90 @@ -0,0 +1,56 @@ +! tests/test_component_manager.f90 (修正版) +program test_component_manager + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + implicit none + + type(cfd_config) :: config + + print *, "=== Component Manager Test (简化版) ===" + print *, "" + + ! 测试1: 基本配置 + print *, "1. Testing basic configuration..." + print *, "-----------------------------------" + + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_wp + + call config_print(config) + print *, "" + + print *, "2. Testing component manager info (简化)..." + print *, "------------------------------------------" + print *, "Component manager functions (简化版本):" + print *, " - Configuration validation available" + print *, " - Component creation framework ready" + print *, "" + + print *, "3. Testing WENO3 configuration..." + print *, "---------------------------------" + + call config_with_reconstruction(config, "weno3", 3) + + print *, "WENO3 configuration:" + print *, " Scheme: ", trim(config%recon_scheme) + print *, " Order: ", config%spatial_order + print *, "" + + ! 测试4: 错误配置测试 + print *, "4. Testing error handling..." + print *, "-----------------------------------" + + config%recon_scheme = "unknown_scheme" + config%flux_type = "unknown_flux" + + print *, "Invalid configuration test:" + print *, " Scheme: ", trim(config%recon_scheme) + print *, " Flux: ", trim(config%flux_type) + print *, "" + + print *, "=== Component manager test completed (简化版) ===" + print *, "下一步: 完善组件管理器功能" + +end program test_component_manager \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_component_manager_physics.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_component_manager_physics.f90 new file mode 100644 index 00000000..f2becca9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_component_manager_physics.f90 @@ -0,0 +1,120 @@ +! tests/test_component_manager_physics.f90 (简化版) +program test_component_manager_physics + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + use component_manager_module, only: component_manager_info, validate_config + + implicit none + + type(cfd_config) :: config + logical :: is_valid + + print *, "=== Component Manager Physics Test (Simplified) ===" + print *, "" + + ! 测试1: 显示组件管理器信息 + print *, "1. Testing component manager info..." + print *, "-------------------------------------" + call component_manager_info() + print *, "" + + ! 测试2: 物理模块测试(默认) + print *, "2. Testing physics module with default configuration..." + print *, "------------------------------------------------------" + + config%verbose = .true. + call config_print(config) + + ! 验证配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Default configuration is valid" + else + print *, "[ERROR] Default configuration is invalid" + end if + print *, "" + + ! 测试3: 测试物理配置 + print *, "3. Testing physics configuration..." + print *, "------------------------------------" + + ! 修改物理参数 + config%equation_type = "linear_advection" + config%problem_type = "linear_advection" + config%wave_speed = 2.5_wp + config%domain_length = 3.0_wp + + print *, "Modified physics configuration:" + print *, " Equation type: ", trim(config%equation_type) + print *, " Problem type: ", trim(config%problem_type) + print *, " Wave speed: ", config%wave_speed + print *, " Domain length: ", config%domain_length + print *, " Physics enabled: ", config%enable_physics + + ! 验证修改后的配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Modified physics configuration is valid" + else + print *, "[ERROR] Modified physics configuration is invalid" + end if + print *, "" + + ! 测试4: 数值组件测试 + print *, "4. Testing numerical components with physics..." + print *, "-----------------------------------------------" + + call config_with_reconstruction(config, "weno3", 3) + config%flux_type = "rusanov" + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Combined physics+numerics configuration is valid" + else + print *, "[ERROR] Combined configuration is invalid" + end if + print *, "" + + ! 测试5: 物理模块禁用测试 + print *, "5. Testing physics module disabled..." + print *, "---------------------------------------" + + config%enable_physics = .false. + config%verbose = .false. + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Configuration valid even with physics disabled" + else + print *, "[ERROR] Configuration should be valid with physics disabled" + end if + print *, "" + + ! 测试6: 错误配置测试 + print *, "6. Testing error handling..." + print *, "-----------------------------" + + config%verbose = .true. + config%enable_physics = .true. + config%recon_scheme = "unknown_scheme" + config%flux_type = "unknown_flux" + config%equation_type = "unknown_equation" + config%problem_type = "unknown_problem" + + is_valid = validate_config(config) + if (.not. is_valid) then + print *, "[OK] Invalid configuration correctly rejected" + else + print *, "[ERROR] Invalid configuration should have been rejected" + end if + print *, "" + + print *, "=== Component Manager Physics Test Summary ===" + print *, "✓ Component manager info works" + print *, "✓ Configuration validation works with physics" + print *, "✓ Error handling works correctly" + print *, "✓ Combined physics+numerics validation works" + print *, "" + print *, "下一步: 集成物理模块到求解器框架" + +end program test_component_manager_physics \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_config_physics.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_config_physics.f90 new file mode 100644 index 00000000..c6fef5c0 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_config_physics.f90 @@ -0,0 +1,141 @@ +! tests/test_config_physics.f90 (修复版) +program test_config_physics + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + + implicit none + + type(cfd_config) :: config + + print *, "=== Configuration Physics Test (Simplified) ===" + print *, "" + + ! 测试1: 默认配置 + print *, "1. Testing default configuration..." + print *, "-----------------------------------" + call config_print(config) + print *, "" + + ! 测试2: 验证基础物理字段 + print *, "2. Testing basic physics fields..." + print *, "----------------------------------" + + print *, "Verifying default physics fields:" + + if (trim(config%equation_type) == "linear_advection") then + print *, " ✓ Default equation type: linear_advection" + else + print *, " ✗ Unexpected equation type: ", trim(config%equation_type) + end if + + if (trim(config%problem_type) == "linear_advection") then + print *, " ✓ Default problem type: linear_advection" + else + print *, " ✗ Unexpected problem type: ", trim(config%problem_type) + end if + + if (abs(config%domain_length - 2.0_wp) < 1e-10_wp) then + print *, " ✓ Default domain length: 2.0" + else + print *, " ✗ Unexpected domain length: ", config%domain_length + end if + + if (config%enable_physics) then + print *, " ✓ Physics enabled by default" + else + print *, " ✗ Physics not enabled by default" + end if + + print *, "" + + ! 测试3: 使用类型绑定的方法(正确的方法名) + print *, "3. Testing type-bound procedures..." + print *, "--------------------------------------" + + call config%set_physics_parameters( & + equation_type="burgers_equation", & + problem_type="sod_shock_tube", & + domain_length=3.0_wp, & + enable_physics=.false.) + + print *, "After set_physics_parameters:" + print *, " Equation type: ", trim(config%equation_type) + print *, " Problem type: ", trim(config%problem_type) + print *, " Domain length: ", config%domain_length + print *, " Physics enabled: ", config%enable_physics + + if (trim(config%equation_type) == "burgers_equation") then + print *, " ✓ Equation type modified successfully via set_physics_parameters" + end if + + if (trim(config%problem_type) == "sod_shock_tube") then + print *, " ✓ Problem type modified successfully via set_physics_parameters" + end if + + if (abs(config%domain_length - 3.0_wp) < 1e-10_wp) then + print *, " ✓ Domain length modified successfully via set_physics_parameters" + end if + + if (.not. config%enable_physics) then + print *, " ✓ Physics disabled successfully via set_physics_parameters" + end if + + print *, "" + + ! 测试4: 调用get_physics_info方法 + print *, "4. Testing get_physics_info method..." + print *, "--------------------------------------" + call config%get_physics_info() + print *, "" + + ! 测试5: 高斯脉冲配置 + print *, "5. Testing Gaussian pulse configuration..." + print *, "-----------------------------------------" + + config%ic_type = "gaussian" + config%pulse_center = 0.6_wp + config%pulse_width = 0.15_wp + + print *, "Gaussian pulse parameters:" + print *, " IC type: ", trim(config%ic_type) + print *, " Center: ", config%pulse_center + print *, " Width: ", config%pulse_width + + if (trim(config%ic_type) == "gaussian") then + print *, " ✓ Gaussian IC type set" + end if + + if (abs(config%pulse_center - 0.6_wp) < 1e-10_wp) then + print *, " ✓ Pulse center set" + end if + + if (abs(config%pulse_width - 0.15_wp) < 1e-10_wp) then + print *, " ✓ Pulse width set" + end if + + print *, "" + + ! 测试6: 重构配置 + print *, "6. Testing reconstruction configuration..." + print *, "------------------------------------------" + + call config_with_reconstruction(config, "weno", 5) + + print *, "Reconstruction configuration:" + print *, " Scheme: ", trim(config%recon_scheme) + print *, " Order: ", config%spatial_order + + if (trim(config%recon_scheme) == "weno" .and. config%spatial_order == 5) then + print *, " ✓ WENO5 configuration successful" + else + print *, " ✗ Reconstruction configuration failed" + end if + + print *, "" + + print *, "=== Configuration Physics Test Complete ===" + print *, "✓ Config module updated with physics support" + print *, "✓ Fields can be directly accessed and modified" + print *, "✓ Type-bound procedures work correctly" + +end program test_config_physics \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_domain_solution.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_domain_solution.f90 new file mode 100644 index 00000000..ff659bac --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_domain_solution.f90 @@ -0,0 +1,102 @@ +! tests/test_domain_solution.f90 +program test_domain_solution + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create, solution_reset + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + real(wp), allocatable :: initial_values(:) + integer :: i + + print *, "=== Domain and Solution Test ===" + print *, "" + + ! 测试1: 不同重构方案的ghost层计算 + print *, "1. Testing ghost layer calculation..." + print *, "--------------------------------------" + + ! ENO3 + call config_with_reconstruction(config, "eno", 3) + config%verbose = .false. + call mesh%init(ncells=10) + domain = domain_create(config, mesh) + print *, "ENO3: nghosts = ", domain%nghosts, " (expected: 3)" + + ! WENO3 + call config_with_reconstruction(config, "weno3", 3) + domain = domain_create(config, mesh) + print *, "WENO3: nghosts = ", domain%nghosts, " (expected: 2)" + + ! WENO5 + call config_with_reconstruction(config, "weno", 5) + domain = domain_create(config, mesh) + print *, "WENO5: nghosts = ", domain%nghosts, " (expected: 3)" + print *, "" + + ! 测试2: Solution数组 + print *, "2. Testing solution arrays..." + print *, "------------------------------" + + call config_with_reconstruction(config, "eno", 3) + config%verbose = .true. + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + domain = domain_create(config, mesh) + call domain%print_info() + print *, "" + + solution = solution_create(domain) + call solution%print_info() + print *, "" + + ! 测试3: 初始化和更新 + print *, "3. Testing initialization and update..." + print *, "----------------------------------------" + + allocate(initial_values(mesh%ncells)) + do i = 1, mesh%ncells + initial_values(i) = sin(2.0_wp * 3.14159265358979_wp * mesh%xcc(i) / mesh%L) + end do + + call solution%initialize(initial_values) + print *, "After initialization:" + print *, " u range: ", minval(solution%u), " to ", maxval(solution%u) + print *, " un range: ", minval(solution%un), " to ", maxval(solution%un) + + ! 修改当前解,测试更新 + solution%u = solution%u * 2.0_wp + call solution%update_old_field() + print *, "After update: max|u - un| = ", maxval(abs(solution%u - solution%un)) + print *, "" + + ! 测试4: 重置 + print *, "4. Testing reset..." + print *, "-------------------" + + call solution_reset(solution) + print *, "After reset:" + print *, " u max: ", maxval(abs(solution%u)) + print *, " un max: ", maxval(abs(solution%un)) + print *, " flux max: ", maxval(abs(solution%flux)) + print *, "" + + deallocate(initial_values) + + print *, "=== Test Summary ===" + print *, "✓ Ghost layer calculation works" + print *, "✓ Domain creation works" + print *, "✓ Solution arrays work" + print *, "✓ Initialization works" + print *, "✓ Field update works" + print *, "✓ Reset works" + print *, "" + print *, "Ready for next step: Implementing Physics modules" + +end program test_domain_solution \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_factory_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_factory_simple.f90 new file mode 100644 index 00000000..db65da7c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_factory_simple.f90 @@ -0,0 +1,58 @@ +! tests/test_factory_simple.f90 (修复版) +program test_factory_simple + use base_modules, only: wp ! ← 添加这行 + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Basic systems + print *, "1. Testing basic systems..." + print *, "-----------------------------" + call config_print(config) + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Creating reconstructors + print *, "2. Testing reconstructors..." + print *, "------------------------------" + + ! 创建并测试ENO重构器 + print *, "Creating ENO reconstructor..." + eno = eno_reconstructor() ! 使用构造函数 + call eno%info() ! 必须调用info方法 + + print *, "" + print *, "Creating WENO3 reconstructor..." + weno3 = weno3_reconstructor() ! 使用构造函数 + call weno3%info() ! 必须调用info方法 + print *, "" + + ! Test 3: Creating flux calculator + print *, "3. Testing flux calculator..." + print *, "-------------------------------" + + print *, "Creating Rusanov flux calculator..." + rusanov = rusanov_flux() ! 使用构造函数 + call rusanov%info() ! 必须调用info方法 + print *, "" + + print *, "=== Factory pattern simple test completed successfully ===" + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_infrastructure.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_infrastructure.f90 new file mode 100644 index 00000000..22fa92d1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_infrastructure.f90 @@ -0,0 +1,56 @@ +! tests/test_infrastructure.f90 (原test_basic_only.f90) +program test_infrastructure + use base_modules, only: wp + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use registry_module, only: registry_init, registry_cleanup, & + register_component_simple, list_components + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "=== 基础设施测试 ===" + print *, "" + + ! 测试1: 配置 + print *, "1. 测试配置模块..." + print *, "-------------------" + call config_print(config) + print *, "" + + ! 测试2: 网格 + print *, "2. 测试网格模块..." + print *, "------------------" + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=5) + print *, "网格初始化:" + print *, " 单元数: ", mesh%ncells + print *, " 节点数: ", mesh%nnodes + print *, " 网格间距: ", mesh%dx + print *, "" + + ! 测试3: 注册系统 + print *, "3. 测试注册系统..." + print *, "------------------" + + call registry_init() + + ! 注册组件(使用简化版本) + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! 列出组件 + call list_components() + print *, "" + + ! 清理 + call registry_cleanup() + + print *, "=== 基础设施测试通过 ===" + print *, "✓ 配置模块工作正常" + print *, "✓ 网格模块工作正常" + print *, "✓ 注册系统工作正常" + +end program test_infrastructure \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_initial_condition.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_initial_condition.f90 new file mode 100644 index 00000000..1c6064b6 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_initial_condition.f90 @@ -0,0 +1,96 @@ +! tests/test_initial_condition.f90 +program test_initial_condition + use base_modules, only: wp + use config_module, only: cfd_config + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create + use ic_factory_module, only: create_initial_condition + use ic_base_module, only: initial_condition + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + class(initial_condition), allocatable :: ic + integer :: i + + print *, "=== 初始条件模块测试 ===" + print *, "" + + ! 创建配置和网格 + config%verbose = .false. + config%recon_scheme = "eno" + config%spatial_order = 3 + + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + domain = domain_create(config, mesh) + solution = solution_create(domain) + + ! 测试1: 阶跃函数初始条件 + print *, "1. 测试阶跃函数初始条件..." + call create_initial_condition("step", ic) + + if (allocated(ic)) then + call ic%apply(solution) + print *, " 成功应用阶跃函数初始条件" + print *, " 解范围: ", minval(solution%u), " 到 ", maxval(solution%u) + + ! 检查结果 + if (abs(maxval(solution%u) - 2.0_wp) < 1e-10_wp .and. & + abs(minval(solution%u) - 1.0_wp) < 1e-10_wp) then + print *, " ✓ 阶跃函数测试通过" + else + print *, " ✗ 阶跃函数测试失败" + end if + end if + + deallocate(ic) + print *, "" + + ! 测试2: 正弦波初始条件 + print *, "2. 测试正弦波初始条件..." + call create_initial_condition("sin", ic) + + if (allocated(ic)) then + call solution%reset() + call ic%apply(solution) + print *, " 成功应用正弦波初始条件" + print *, " 解范围: ", minval(solution%u), " 到 ", maxval(solution%u) + print *, " ✓ 正弦波测试通过" + end if + + deallocate(ic) + print *, "" + + ! 测试3: 高斯脉冲初始条件 + print *, "3. 测试高斯脉冲初始条件..." + call create_initial_condition("gaussian", ic) + + if (allocated(ic)) then + call solution%reset() + call ic%apply(solution) + print *, " 成功应用高斯脉冲初始条件" + print *, " 解范围: ", minval(solution%u), " 到 ", maxval(solution%u) + print *, " ✓ 高斯脉冲测试通过" + end if + + deallocate(ic) + print *, "" + + ! 测试4: 错误处理 + print *, "4. 测试错误处理..." + call create_initial_condition("unknown", ic) + + if (allocated(ic)) then + print *, " ✓ 错误处理测试通过(使用了默认初始条件)" + else + print *, " ✗ 错误处理测试失败" + end if + + print *, "" + print *, "=== 初始条件模块测试完成 ===" + +end program test_initial_condition \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_physics.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_physics.f90 new file mode 100644 index 00000000..784a3ffc --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_physics.f90 @@ -0,0 +1,20 @@ +! tests/test_physics.f90 +program test_physics + use base_modules, only: wp, ip + implicit none + + print *, "=== 物理模块测试(简化版)===" + print *, "" + + print *, "1. 测试基本物理概念..." + print *, " ✓ 物理模块占位符" + print *, "" + + print *, "2. 测试阶跃函数逻辑..." + print *, " ✓ 阶跃函数逻辑占位符" + print *, "" + + print *, "=== 物理模块测试完成 ===" + print *, "注意: 这是简化测试,物理模块尚未完全集成" + +end program test_physics \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_physics_solver.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_physics_solver.f90 new file mode 100644 index 00000000..0c83641f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_physics_solver.f90 @@ -0,0 +1,85 @@ +! tests/test_physics_solver.f90 (简化修正版) +program test_physics_solver + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction + use mesh_module, only: mesh_type + use physics_solver_module, only: physics_solver, SOLVER_COMPLETED + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(physics_solver) :: psolver + real(wp) :: final_time + integer :: state + + print *, "=== Physics Solver Test (简化版) ===" + print *, "" + + ! 测试1: 创建物理求解器 + print *, "1. Creating physics solver..." + print *, "-----------------------------" + + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%dt = 0.01_wp + config%enable_physics = .true. + + print *, "Configuration:" + print *, " Scheme: ", trim(config%recon_scheme) + print *, " dt: ", config%dt + print *, " Physics enabled: ", config%enable_physics + + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + ! 设置求解器配置和网格 + psolver%config = config + psolver%mesh = mesh + + print *, " Solver created successfully" + print *, "" + + ! 测试2: 初始化 + print *, "2. Initializing physics solver..." + print *, "---------------------------------" + + call psolver%initialize() + state = psolver%get_state() + + print *, " State after initialization: ", state + print *, " Expected: initialized (1)" + print *, "" + + ! 测试3: 运行一小段时间 + print *, "3. Running physics solver (short time)..." + print *, "------------------------------------------" + + call psolver%run_to_time(0.02_wp) + state = psolver%get_state() + + print *, " State after run: ", state + print *, " Expected: completed (3)" + print *, " Current time: ", psolver%current_time + print *, " Current step: ", psolver%current_step + print *, "" + + ! 测试4: 清理 + print *, "4. Testing cleanup..." + print *, "----------------------" + + call psolver%cleanup() + state = psolver%get_state() + + print *, " State after cleanup: ", state + print *, " Expected: uninitialized (0)" + print *, "" + + ! 结果验证 + print *, "=== Test Summary ===" + if (state == 0) then + print *, "✓ All basic tests passed" + else + print *, "✗ Some tests failed" + end if + +end program test_physics_solver \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_physics_solver_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_physics_solver_simple.f90 new file mode 100644 index 00000000..13312efd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_physics_solver_simple.f90 @@ -0,0 +1,161 @@ +! tests/test_physics_solver_simple.f90 +program test_physics_solver_simple + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction + use mesh_module, only: mesh_type + use physics_solver_module, only: physics_solver, SOLVER_COMPLETED + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(physics_solver) :: solver + real(wp) :: final_time, final_step + integer :: state + + print *, "=========================================" + print *, " 简单物理求解器测试" + print *, "=========================================" + print *, "" + + ! 步骤1: 配置 + print *, "[步骤1] 配置求解器..." + print *, "---------------------" + + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%dt = 0.01_wp + config%final_time = 0.1_wp + config%wave_speed = 1.0_wp + config%ic_type = "step" + config%boundary_type = "periodic" + config%equation_type = "linear_advection" + config%problem_type = "linear_advection" + config%enable_physics = .true. + config%domain_length = 1.0_wp + + print *, "配置参数:" + print *, " 重构格式: ", trim(config%recon_scheme) + print *, " 时间步长: ", config%dt + print *, " 最终时间: ", config%final_time + print *, " 波速: ", config%wave_speed + print *, "" + + ! 步骤2: 创建网格 + print *, "[步骤2] 创建网格..." + print *, "-------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + + print *, "网格信息:" + print *, " 单元数: ", mesh%ncells + print *, " 节点数: ", mesh%nnodes + print *, " 网格间距: ", mesh%dx + print *, "" + + ! 步骤3: 创建求解器 + print *, "[步骤3] 创建求解器..." + print *, "---------------------" + + solver = physics_solver(config, mesh) + + print *, "求解器创建成功" + print *, " 初始状态: ", solver%get_state() + print *, "" + + ! 步骤4: 初始化 + print *, "[步骤4] 初始化求解器..." + print *, "-----------------------" + + call solver%initialize() + + state = solver%get_state() + print *, "初始化完成" + print *, " 状态: ", state + print *, " 当前时间: ", solver%current_time + print *, " 当前步数: ", solver%current_step + print *, "" + + ! 步骤5: 运行求解器 + print *, "[步骤5] 运行求解器..." + print *, "---------------------" + + call solver%run_to_time(config%final_time) + + state = solver%get_state() + print *, "运行完成" + print *, " 状态: ", state + print *, " 最终时间: ", solver%current_time + print *, " 总步数: ", solver%current_step + print *, "" + + ! 步骤6: 保存结果 + print *, "[步骤6] 保存结果..." + print *, "-------------------" + + final_time = solver%current_time + final_step = real(solver%current_step, wp) + state = solver%get_state() + + print *, "保存的结果:" + print *, " 状态: ", state + print *, " 时间: ", final_time + print *, " 步数: ", final_step + print *, "" + + ! 步骤7: 清理求解器 + print *, "[步骤7] 清理求解器..." + print *, "---------------------" + + call solver%cleanup() + + print *, "清理后状态:" + print *, " 状态: ", solver%get_state() + print *, " 时间: ", solver%current_time + print *, " 步数: ", solver%current_step + print *, "" + + ! 步骤8: 验证结果 + print *, "[步骤8] 验证结果..." + print *, "-------------------" + + print *, "验证标准:" + print *, " 1. 运行后状态应为 COMPLETED (", SOLVER_COMPLETED, ")" + print *, " 2. 最终时间应接近 ", config%final_time + print *, " 3. 步数应大于 0" + print *, "" + + if (state == SOLVER_COMPLETED) then + print *, "✓ 状态验证通过: COMPLETED" + else + print *, "✗ 状态验证失败: 期望 ", SOLVER_COMPLETED, ", 实际 ", state + end if + + if (abs(final_time - config%final_time) < 1e-5_wp) then + print *, "✓ 时间验证通过: ", final_time, " ≈ ", config%final_time + else + print *, "✗ 时间验证失败: ", final_time, " ≠ ", config%final_time + end if + + if (final_step > 0) then + print *, "✓ 步数验证通过: ", final_step, " > 0" + else + print *, "✗ 步数验证失败: ", final_step, " ≤ 0" + end if + + print *, "" + + ! 最终判断 + if (state == SOLVER_COMPLETED .and. & + abs(final_time - config%final_time) < 1e-5_wp .and. & + final_step > 0) then + print *, "=========================================" + print *, " 所有测试通过! ✓" + print *, "=========================================" + else + print *, "=========================================" + print *, " 测试失败 ✗" + print *, "=========================================" + end if + +end program test_physics_solver_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_registry.f90 new file mode 100644 index 00000000..e82651ff --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_registry.f90 @@ -0,0 +1,87 @@ +! tests/test_registry.f90 (原test_minimal_simple.f90) +program test_registry + use base_modules, only: wp + use registry_module + use config_module + use mesh_module + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== 注册系统功能测试 ===" + print *, "" + + ! 测试1: 配置系统 + print *, "1. 测试配置系统" + print *, "--------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! 测试2: 网格系统 + print *, "2. 测试网格系统" + print *, "--------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! 测试3: 注册系统 + print *, "3. 测试注册系统" + print *, "--------------" + + call registry_init() + + ! 注册组件 + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call list_components() + print *, "注册表大小: ", registry_get_size() + print *, "" + + ! 测试组件查找 + print *, "4. 测试组件查找" + print *, "--------------" + + if (has_component_simple("reconstructor", "eno")) then + print *, "找到: reconstructor.eno" + else + print *, "未找到: reconstructor.eno" + end if + + if (has_component_simple("reconstructor", "unknown")) then + print *, "找到: reconstructor.unknown" + else + print *, "未找到: reconstructor.unknown" + end if + print *, "" + + ! 测试获取可用组件 + print *, "5. 测试注册系统功能" + print *, "------------------" + print *, "注册表已初始化: ", registry_is_initialized() + print *, "组件数量: ", registry_get_size() + print *, "" + + ! 清理 + call registry_cleanup() + + print *, "=== 注册系统测试完成 ===" + +end program test_registry \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_simple_link.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_simple_link.f90 new file mode 100644 index 00000000..71cc614e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_simple_link.f90 @@ -0,0 +1,78 @@ +! tests/test_simple_link.f90 +program test_simple_link + use base_modules, only: wp ! ← 添加这行 + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call registry_init() + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call list_components() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component_simple("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component_simple("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Cleanup + call registry_cleanup() + + print *, "=== Minimal test completed successfully ===" + +end program test_simple_link \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_solver_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_solver_base.f90 new file mode 100644 index 00000000..6cfe47e4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_solver_base.f90 @@ -0,0 +1,99 @@ +! tests/test_solver_base.f90 (修复版) +program test_solver_base + ! 所有 USE 语句必须在程序开始处 + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + use mesh_module, only: mesh_type + use solver_base_module, only: solver_base, SOLVER_UNINITIALIZED, & + SOLVER_INITIALIZED, SOLVER_COMPLETED, SOLVER_ERROR + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(solver_base) :: solver + integer :: state + + print *, "=== Solver Base Test ===" + print *, "" + + ! 测试1: 创建求解器 + print *, "1. Creating solver..." + print *, "----------------------" + + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%dt = 0.01_wp + + call config_print(config) + + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + solver = solver_base(config, mesh) + call solver%print_info() + print *, "" + + ! 测试2: 初始化 + print *, "2. Initializing solver..." + print *, "-------------------------" + + call solver%initialize() + state = solver%get_state() + print *, "State after initialization: ", state + print *, "Expected: ", SOLVER_INITIALIZED + print *, "Match? ", state == SOLVER_INITIALIZED + print *, "Error message: '", trim(solver%get_error()), "'" + print *, "" + + ! 测试3: 运行求解器 + print *, "3. Running solver..." + print *, "--------------------" + + call solver%run_to_time(0.05_wp) + state = solver%get_state() + print *, "State after run: ", state + print *, "Expected: ", SOLVER_COMPLETED + print *, "Match? ", state == SOLVER_COMPLETED + print *, "Current time: ", solver%current_time + print *, "Current step: ", solver%current_step + print *, "" + + ! 测试4: 再次运行(从已完成状态) + print *, "4. Running again from completed state..." + print *, "----------------------------------------" + + ! 需要先清理才能重新运行 + call solver%cleanup() + call solver%initialize() + call solver%run_to_time(0.1_wp) + + call solver%print_info() + print *, "" + + ! 测试5: 错误处理 + print *, "5. Testing error states..." + print *, "--------------------------" + + ! 创建一个未初始化的求解器 + call solver%cleanup() + state = solver%get_state() + print *, "Uninitialized state: ", state + print *, "Expected: ", SOLVER_UNINITIALIZED + print *, "Match? ", state == SOLVER_UNINITIALIZED + + ! 尝试运行未初始化的求解器 + call solver%run_to_time(0.01_wp) + state = solver%get_state() + print *, "State after error: ", state + print *, "Expected: ", SOLVER_ERROR + print *, "Match? ", state == SOLVER_ERROR + print *, "Error message: '", trim(solver%get_error()), "'" + print *, "" + + print *, "=== Solver Base Test Complete ===" + print *, "✓ Solver base class works" + print *, "✓ State management works" + print *, "✓ Time stepping framework works" + print *, "✓ Error handling works" + +end program test_solver_base \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_solver_framework.f90 b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_solver_framework.f90 new file mode 100644 index 00000000..6754323d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03f/tests/test_solver_framework.f90 @@ -0,0 +1,91 @@ +! tests/test_solver_framework.f90 +program test_solver_framework + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use mesh_module, only: mesh_type + use solver_module, only: cfd_solver, solver_create, solver_run, solver_cleanup + use solver_module, only: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, & + SOLVER_COMPLETED, SOLVER_ERROR + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(cfd_solver) :: solver + + print *, "=== 求解器框架测试 ===" + print *, "" + + ! 测试1: 基本创建 + print *, "1. 测试求解器创建..." + print *, "----------------------" + + ! 创建配置 + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.0_real64 + config%dt = 0.01_real64 + + call config_print(config) + print *, "" + + ! 创建网格 + call mesh%init(xmin=0.0_real64, xmax=2.0_real64, ncells=20) + call mesh%print_info() + print *, "" + + ! 创建求解器 + solver = solver_create(config, mesh) + print *, "✓ 求解器创建成功" + print *, " 状态: ", solver%get_state() + print *, "" + + ! 测试2: 求解器初始化 + print *, "2. 测试求解器初始化..." + print *, "------------------------" + + call solver%initialize() + print *, "✓ 求解器初始化完成" + print *, " 状态: ", solver%get_state() + print *, " 错误信息: '", trim(solver%get_error()), "'" + print *, "" + + ! 测试3: 简单运行 + print *, "3. 测试求解器运行..." + print *, "----------------------" + + call solver_run(solver, 0.05_real64) ! 运行到0.05秒 + print *, "✓ 求解器运行完成" + print *, " 最终状态: ", solver%get_state() + print *, "" + + ! 测试4: 清理 + print *, "4. 测试求解器清理..." + print *, "----------------------" + + call solver_cleanup(solver) + print *, "✓ 求解器清理完成" + print *, " 状态: ", solver%get_state() + print *, "" + + ! 测试5: 错误处理 + print *, "5. 测试错误处理..." + print *, "-------------------" + + ! 尝试重复初始化 + call solver%initialize() + print *, " 重复初始化状态: ", solver%get_state() + print *, " 错误信息: '", trim(solver%get_error()), "'" + + call solver_cleanup(solver) + print *, "" + + print *, "=== 框架测试总结 ===" + print *, "✓ 求解器创建/初始化/运行/清理流程验证完成" + print *, "✓ 状态管理正常工作" + print *, "✓ 错误处理机制就绪" + print *, "" + print *, "下一步: 添加实际数值计算功能" + +end program test_solver_framework \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03g/CMakeLists.txt new file mode 100644 index 00000000..55859dc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 4.2.1) +project(FortranCFD VERSION 1.0.0 LANGUAGES Fortran) + +set(CMAKE_Fortran_STANDARD 2008) +set(CMAKE_Fortran_STANDARD_REQUIRED ON) + +message(STATUS "Project: ${PROJECT_NAME} Version: ${PROJECT_VERSION}") +message(STATUS "Compiler: ${CMAKE_Fortran_COMPILER}") +message(STATUS "Compiler ID: ${CMAKE_Fortran_COMPILER_ID}") +message(STATUS "Compiler Version: ${CMAKE_Fortran_COMPILER_VERSION}") + + +set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 共享库(.so/.dylib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +add_subdirectory(src) +add_subdirectory(tests) +add_subdirectory(examples) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/README.md b/example/1d-linear-convection/weno3/fortran/registry/03g/README.md new file mode 100644 index 00000000..c4889757 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/README.md @@ -0,0 +1,221 @@ +# Fortran CFD Project + +基于工厂模式和注册系统的CFD求解器框架。 + +## 项目结构 + +``` +fortran_cfd/ +├── CMakeLists.txt # 根CMake配置 +├── scripts/ # 构建脚本 +│ ├── build.py # Python构建脚本(推荐) +│ ├── build.bat # Batch包装脚本 +│ ├── build.ps1 # PowerShell包装脚本 +│ └── requirements.txt # Python依赖 +├── src/ # 源代码 +│ ├── CMakeLists.txt +│ ├── core/ # 核心模块(注册系统、工厂接口) +│ ├── infrastructure/ # 基础设施(配置、网格) +│ └── numerics/ # 数值方法 +├── tests/ # 测试 +│ ├── CMakeLists.txt +│ ├── test_minimal_simple.f90 # 简单测试 +│ └── test_factory.f90 # 工厂模式测试 +└── README.md # 项目说明(本文件) +``` + +## 快速开始 + +### 使用Python脚本(推荐) + +```bash +# 进入脚本目录 +cd scripts + +# 基本构建 +python build.py + +# 清理并构建 +python build.py --clean + +# 发布构建 +python build.py --build-type Release --clean + +# 并行构建(使用所有核心) +python build.py -j + +# 不运行测试 +python build.py --no-tests + +# 查看帮助 +python build.py --help +``` + +### 使用批处理脚本 + +```bash +cd scripts +.\build.bat +``` + +### 使用PowerShell脚本 + +```powershell +cd scripts +.\build.ps1 +``` + +## 手动构建 + +```bash +# 设置Intel环境 +cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell' + +# 配置项目 +mkdir build +cd build +cmake -G "Visual Studio 17 2022" -A x64 -T fortran=ifx .. + +# 构建项目 +cmake --build . --config Debug + +# 运行测试 +Debug\test_simple.exe +Debug\test_factory.exe +``` + +## 功能特性 + +✅ 组件注册系统 +✅ 工厂模式 +✅ ENO/WENO重构器 +✅ Rusanov通量计算器 +✅ 可扩展架构 +✅ 自动化测试 + +## 依赖 + +- Intel oneAPI(包含ifx编译器) +- CMake 3.12+ +- Python 3.6+(仅用于构建脚本) + +## 源代码文件 + +### 核心模块 +- `src/core/registry.f90` - 组件注册系统 +- `src/core/factory_interfaces.f90` - 工厂接口 + +### 基础设施 +- `src/infrastructure/config.f90` - 配置管理 +- `src/infrastructure/mesh.f90` - 网格生成 + +### 数值方法 +- `src/numerics/reconstructor/base.f90` - 重构器基类 +- `src/numerics/reconstructor/eno.f90` - ENO重构器 +- `src/numerics/reconstructor/weno3.f90` - WENO-3重构器 +- `src/numerics/flux/base.f90` - 通量计算器基类 +- `src/numerics/flux/rusanov.f90` - Rusanov通量 + +### 测试 +- `tests/test_minimal_simple.f90` - 简单功能测试 +- `tests/test_factory.f90` - 工厂模式测试 + +## 构建脚本 + +### Python脚本 (build.py) +主构建脚本,支持: +- 自动设置Intel oneAPI环境 +- 参数化构建选项 +- 并行编译 +- 自动化测试 +- 彩色输出 + +### Batch脚本 (build.bat) +Windows批处理包装器,调用Python脚本。 + +### PowerShell脚本 (build.ps1) +PowerShell包装器,调用Python脚本。 + +## 示例输出 + +成功构建后,会看到类似输出: + +``` +============================================================ + Fortran CFD Project Builder +============================================================ + +[1/4] Setting up Intel Fortran compiler... +✓ Intel oneAPI environment configured + +[2/4] Preparing build directory... +✓ Cleaned build directory + +[3/4] Configuring project... + $ cmake -G Visual Studio 17 2022 -A x64 -T fortran=ifx -DCMAKE_BUILD_TYPE=Debug .. +✓ CMake configuration successful + +[4/4] Building project... + $ cmake --build . --config Debug +✓ Build completed in 12.3 seconds + +============================================================ + Running Tests +============================================================ + +[TEST] Simple functionality test... +✓ Simple test passed + +[TEST] Factory pattern test... +✓ Factory test passed + +============================================================ + Build Summary +============================================================ +✓ Build directory: D:\path\to\build +✓ Build type: Debug +✓ Total time: 15.2s +``` + +## 开发指南 + +### 添加新组件 + +1. 在相应模块中创建Fortran源文件 +2. 实现工厂函数 +3. 使用`register_component_with_factory`注册组件 +4. 添加测试 + +### 扩展架构 + +项目采用模块化设计: +1. **注册系统** - 管理所有可用的组件 +2. **工厂模式** - 动态创建组件实例 +3. **抽象接口** - 确保组件兼容性 +4. **配置驱动** - 通过配置文件控制行为 + +## 故障排除 + +### 常见问题 + +1. **Intel环境未找到** + - 检查Intel oneAPI安装路径 + - 手动运行`setvars.bat` + +2. **CMake配置失败** + - 确保使用正确的生成器:`Visual Studio 17 2022` + - 检查Fortran编译器是否可用 + +3. **构建失败** + - 检查依赖项是否完整 + - 查看详细的错误信息 + +### 调试建议 + +- 使用`--verbose`参数获取详细输出 +- 检查`build/CMakeCache.txt`了解配置详情 +- 查看编译器错误日志 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/examples/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03g/examples/CMakeLists.txt new file mode 100644 index 00000000..cff65214 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/examples/CMakeLists.txt @@ -0,0 +1,39 @@ +# examples/CMakeLists.txt (修正版) +message(STATUS "配置示例程序...") + +# 主示例程序:ENO/WENO对比(当前版本) +add_executable(run_eno_weno + run_eno_weno.f90 +) + +target_link_libraries(run_eno_weno + PRIVATE + solver + infrastructure + core + physics + manager + results + boundary + initial_condition +) + +# 集成版本示例 +add_executable(run_eno_weno_integrated + run_eno_weno_integrated.f90 +) + +target_link_libraries(run_eno_weno_integrated + PRIVATE + solver_integrated # 主要模块 + infrastructure + base + boundary # 集成求解器需要的边界条件 + initial_condition # 集成求解器需要的初始条件 +) + +install(TARGETS run_eno_weno run_eno_weno_integrated + RUNTIME DESTINATION bin/examples +) + +message(STATUS "示例程序配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/examples/run_eno_weno.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/examples/run_eno_weno.f90 new file mode 100644 index 00000000..ffa6ff9d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/examples/run_eno_weno.f90 @@ -0,0 +1,327 @@ +! examples/run_eno_weno.f90 (修正版) +program run_eno_weno + ! Example program: ENO/WENO comparison analysis + + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction, config_print + use mesh_module, only: mesh_type + use registry_module, only: registry_init, registry_cleanup, initialize_default_components + use physics_solver_module, only: physics_solver, SOLVER_COMPLETED + use results_module, only: results_saver, save_results ! 修改:只导入需要的内容 + + implicit none + + type(cfd_config) :: config_eno3, config_weno3, config_weno5 + type(mesh_type) :: mesh + type(physics_solver) :: solver_eno3, solver_weno3, solver_weno5 + + ! 结果保存器 + type(results_saver) :: saver ! 直接声明类型 + + ! Variables to save results + real(wp) :: time_eno3, time_weno3, time_weno5 + integer :: steps_eno3, steps_weno3, steps_weno5 + integer :: state_eno3, state_weno3, state_weno5 + logical :: all_success + + ! Debug: print start marker + print *, "==========================================" + print *, "START: ENO/WENO Comparison Analysis" + print *, "==========================================" + print *, "" + + ! Step 0: Initialize system + print *, "[STEP 0] Initializing system..." + print *, "--------------------------------" + + call registry_init(verbose=.true.) + print *, "Registry initialized" + + call initialize_default_components() + print *, "Default components registered" + print *, "" + + ! Step 1: Create mesh + print *, "[STEP 1] Creating computational mesh..." + print *, "----------------------------------------" + + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=40) + call mesh%print_info() + print *, "" + + ! 创建结果保存器 - 使用构造函数而不是赋值 + saver = results_saver("results", .true.) ! 直接使用构造函数 + + ! ========== ENO3 Solver ========== + print *, "[STEP 2] Configuring and running ENO3 solver..." + print *, "-----------------------------------------------" + + ! Configure ENO3 + config_eno3%verbose = .true. + config_eno3%ic_type = "step" + config_eno3%wave_speed = 1.0_wp + config_eno3%final_time = 0.625_wp + config_eno3%dt = 0.0025_wp + config_eno3%rk_order = 2 + config_eno3%boundary_type = "periodic" + config_eno3%equation_type = "linear_advection" + config_eno3%problem_type = "linear_advection" + config_eno3%enable_physics = .true. + config_eno3%domain_length = 2.0_wp + + call config_with_reconstruction(config_eno3, "eno", 3) + + print *, "ENO3 configuration:" + call config_print(config_eno3) + print *, "" + + ! Create and run ENO3 solver + print *, "Creating ENO3 solver instance..." + ! 替换这三行: + ! call physics_solver_constructor(solver_eno3, config_eno3, mesh) + + ! 改为: + solver_eno3 = physics_solver(config_eno3, mesh) + + print *, "Initializing ENO3 solver..." + call solver_eno3%initialize() + + print *, "Running ENO3 solver..." + call solver_eno3%run_to_time(config_eno3%final_time) + + ! Immediately save ENO3 results + time_eno3 = solver_eno3%current_time + steps_eno3 = solver_eno3%current_step + state_eno3 = solver_eno3%get_state() + + print *, "ENO3 solver completed" + print *, " Final time: ", time_eno3 + print *, " Total steps: ", steps_eno3 + print *, " State: ", state_eno3 + print *, "" + + ! 保存ENO3结果到文件 + call save_results(saver, "ENO3", & + solver_eno3%config, solver_eno3%mesh, solver_eno3%domain, & + solver_eno3%solution, & + solver_eno3%current_time, solver_eno3%current_step, solver_eno3%get_state()) + + ! ========== WENO3 Solver ========== + print *, "[STEP 3] Configuring and running WENO3 solver..." + print *, "------------------------------------------------" + + ! Configure WENO3 + config_weno3%verbose = .true. + config_weno3%ic_type = "step" + config_weno3%wave_speed = 1.0_wp + config_weno3%final_time = 0.625_wp + config_weno3%dt = 0.0025_wp + config_weno3%rk_order = 2 + config_weno3%boundary_type = "periodic" + config_weno3%equation_type = "linear_advection" + config_weno3%problem_type = "linear_advection" + config_weno3%enable_physics = .true. + config_weno3%domain_length = 2.0_wp + + call config_with_reconstruction(config_weno3, "weno3", 3) + + print *, "WENO3 configuration:" + call config_print(config_weno3) + print *, "" + + ! Create and run WENO3 solver + print *, "Creating WENO3 solver instance..." + call physics_solver_constructor(solver_weno3, config_weno3, mesh) + + print *, "Initializing WENO3 solver..." + call solver_weno3%initialize() + + print *, "Running WENO3 solver..." + call solver_weno3%run_to_time(config_weno3%final_time) + + ! Immediately save WENO3 results + time_weno3 = solver_weno3%current_time + steps_weno3 = solver_weno3%current_step + state_weno3 = solver_weno3%get_state() + + print *, "WENO3 solver completed" + print *, " Final time: ", time_weno3 + print *, " Total steps: ", steps_weno3 + print *, " State: ", state_weno3 + print *, "" + + ! 保存WENO3结果到文件 + call save_results(saver, "WENO3", & + solver_weno3%config, solver_weno3%mesh, solver_weno3%domain, & + solver_weno3%solution, time_weno3, steps_weno3, state_weno3) + + ! ========== WENO5 Solver ========== + print *, "[STEP 4] Configuring and running WENO5 solver..." + print *, "------------------------------------------------" + + ! Configure WENO5 + config_weno5%verbose = .true. + config_weno5%ic_type = "step" + config_weno5%wave_speed = 1.0_wp + config_weno5%final_time = 0.625_wp + config_weno5%dt = 0.0025_wp + config_weno5%rk_order = 2 + config_weno5%boundary_type = "periodic" + config_weno5%equation_type = "linear_advection" + config_weno5%problem_type = "linear_advection" + config_weno5%enable_physics = .true. + config_weno5%domain_length = 2.0_wp + + call config_with_reconstruction(config_weno5, "weno", 5) + + print *, "WENO5 configuration:" + call config_print(config_weno5) + print *, "" + + ! Create and run WENO5 solver + print *, "Creating WENO5 solver instance..." + call physics_solver_constructor(solver_weno5, config_weno5, mesh) + + print *, "Initializing WENO5 solver..." + call solver_weno5%initialize() + + print *, "Running WENO5 solver..." + call solver_weno5%run_to_time(config_weno5%final_time) + + ! Immediately save WENO5 results + time_weno5 = solver_weno5%current_time + steps_weno5 = solver_weno5%current_step + state_weno5 = solver_weno5%get_state() + + print *, "WENO5 solver completed" + print *, " Final time: ", time_weno5 + print *, " Total steps: ", steps_weno5 + print *, " State: ", state_weno5 + print *, "" + + ! 保存WENO5结果到文件 + call save_results(saver, "WENO5", & + solver_weno5%config, solver_weno5%mesh, solver_weno5%domain, & + solver_weno5%solution, time_weno5, steps_weno5, state_weno5) + + ! ========== Results Summary ========== + print *, "==========================================" + print *, " RESULTS SUMMARY" + print *, "==========================================" + print *, "" + + print *, "Solver Performance Comparison:" + print *, "------------------------------" + + print *, "ENO3:" + print *, " Final time: ", time_eno3 + print *, " Total steps: ", steps_eno3 + print *, " State: ", state_eno3 + print *, " Results saved to: results_ENO3_40.dat" + print *, "" + + print *, "WENO3:" + print *, " Final time: ", time_weno3 + print *, " Total steps: ", steps_weno3 + print *, " State: ", state_weno3 + print *, " Results saved to: results_WENO3_40.dat" + print *, "" + + print *, "WENO5:" + print *, " Final time: ", time_weno5 + print *, " Total steps: ", steps_weno5 + print *, " State: ", state_weno5 + print *, " Results saved to: results_WENO5_40.dat" + print *, "" + + ! ========== Final Judgment ========== + print *, "==========================================" + print *, " FINAL JUDGMENT" + print *, "==========================================" + print *, "" + + all_success = (state_eno3 == SOLVER_COMPLETED) .and. & + (state_weno3 == SOLVER_COMPLETED) .and. & + (state_weno5 == SOLVER_COMPLETED) + + if (all_success) then + print *, "✓ ALL SOLVERS SUCCESSFULLY COMPLETED!" + print *, "" + print *, "Parameter Summary:" + print *, " Grid cells: ", mesh%ncells + print *, " Time step: ", config_eno3%dt + print *, " Final time: ", config_eno3%final_time + print *, " Wave speed: ", config_eno3%wave_speed + print *, " IC type: ", trim(config_eno3%ic_type) + print *, "" + print *, "Performance Comparison:" + print *, " ENO3: ", steps_eno3, " steps" + print *, " WENO3: ", steps_weno3, " steps" + print *, " WENO5: ", steps_weno5, " steps" + print *, "" + print *, "To visualize results:" + print *, " python ../python/plot_results.py --auto" + else + print *, "✗ SOME SOLVERS FAILED" + print *, "" + print *, "Failure Analysis:" + if (state_eno3 /= SOLVER_COMPLETED) then + print *, " • ENO3 failed with state: ", state_eno3 + end if + if (state_weno3 /= SOLVER_COMPLETED) then + print *, " • WENO3 failed with state: ", state_weno3 + end if + if (state_weno5 /= SOLVER_COMPLETED) then + print *, " • WENO5 failed with state: ", state_weno5 + end if + end if + + print *, "" + print *, "==========================================" + print *, " ANALYSIS COMPLETE" + print *, "==========================================" + + ! ========== Cleanup ========== + print *, "" + print *, "[STEP 5] Cleaning up system..." + print *, "--------------------------------" + + call solver_eno3%cleanup() + call solver_weno3%cleanup() + call solver_weno5%cleanup() + call registry_cleanup() + + print *, "All solvers cleaned up" + print *, "Registry cleaned up" + print *, "" + + ! ========== Wait for user input before exit ========== + print *, "==========================================" + print *, "Press ENTER to exit..." + print *, "==========================================" + + ! Wait for user input (uncomment if needed) + ! read(*,*) + + ! Alternative: add a small delay + print *, "Program will exit in 3 seconds..." + call sleep(3) + + print *, "" + print *, "==========================================" + print *, " PROGRAM END" + print *, "==========================================" + +contains + + ! 辅助函数:显式创建physics_solver(如果原始代码使用函数而不是子程序) + subroutine physics_solver_constructor(solver, config, mesh) + type(physics_solver), intent(out) :: solver + type(cfd_config), intent(in) :: config + type(mesh_type), intent(in) :: mesh + + ! 使用赋值构造函数(如果physics_solver_module中有相应的接口) + solver = physics_solver(config, mesh) + end subroutine physics_solver_constructor + +end program run_eno_weno \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/examples/run_eno_weno_integrated.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/examples/run_eno_weno_integrated.f90 new file mode 100644 index 00000000..77629305 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/examples/run_eno_weno_integrated.f90 @@ -0,0 +1,176 @@ +! examples/run_eno_weno_integrated.f90 (完整修复版) +program run_eno_weno_integrated + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction, config_print + use mesh_module, only: mesh_type + use solver_integrated_module, only: integrated_solver, SOLVER_INITIALIZED, SOLVER_COMPLETED + + implicit none + + type(cfd_config) :: config_eno3, config_weno3, config_weno5 + type(mesh_type) :: mesh + type(integrated_solver) :: solver_eno3, solver_weno3, solver_weno5 + + ! 结果变量 + real(wp) :: time_eno3, time_weno3, time_weno5 + integer :: steps_eno3, steps_weno3, steps_weno5 + integer :: state_eno3, state_weno3, state_weno5 + logical :: all_success + + ! 定义常量(避免导入冲突) + integer, parameter :: SOLVER_READY = 0 + integer, parameter :: SOLVER_RUNNING = 2 + integer, parameter :: SOLVER_ERROR = -1 + + print *, "==========================================" + print *, "ENO/WENO 对比分析 (集成版本)" + print *, "==========================================" + print *, "" + + ! 步骤1: 创建网格 + print *, "[STEP 1] 创建计算网格..." + print *, "-----------------------------------" + + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=40) + call mesh%print_info() + print *, "" + + ! ========== ENO3 求解器 ========== + print *, "[STEP 2] 配置和运行 ENO3 求解器..." + print *, "-----------------------------------" + + ! 初始化配置 + config_eno3%verbose = .true. + config_eno3%ic_type = "step" + config_eno3%wave_speed = 1.0_wp + config_eno3%final_time = 0.625_wp + config_eno3%dt = 0.0025_wp + config_eno3%boundary_type = "periodic" + config_eno3%equation_type = "linear_advection" + config_eno3%problem_type = "linear_advection" + config_eno3%domain_length = 2.0_wp + config_eno3%enable_physics = .true. + + call config_with_reconstruction(config_eno3, "eno", 3) + call config_print(config_eno3) + print *, "" + + ! 创建和运行求解器 + solver_eno3%config = config_eno3 + solver_eno3%mesh = mesh + + ! 控制数据模式 (当前使用简单数据) + call solver_eno3%enable_real_data(.false.) ! 设置为false使用简单数据 + + call solver_eno3%initialize() + call solver_eno3%run_to_time(config_eno3%final_time) + + ! 获取结果 + time_eno3 = solver_eno3%current_time + steps_eno3 = solver_eno3%current_step + state_eno3 = solver_eno3%get_state() + + print *, "ENO3 完成:" + print *, " 最终时间: ", time_eno3 + print *, " 总步数: ", steps_eno3 + print *, " 状态: ", state_eno3 + print *, "" + + ! ========== WENO3 求解器 ========== + print *, "[STEP 3] 配置和运行 WENO3 求解器..." + print *, "-----------------------------------" + + ! 配置 (复制ENO3配置,只改重构格式) + config_weno3 = config_eno3 + call config_with_reconstruction(config_weno3, "weno3", 3) + + ! 运行 + solver_weno3%config = config_weno3 + solver_weno3%mesh = mesh + call solver_weno3%enable_real_data(.false.) + + call solver_weno3%initialize() + call solver_weno3%run_to_time(config_weno3%final_time) + + time_weno3 = solver_weno3%current_time + steps_weno3 = solver_weno3%current_step + state_weno3 = solver_weno3%get_state() + + print *, "WENO3 完成:" + print *, " 最终时间: ", time_weno3 + print *, " 总步数: ", steps_weno3 + print *, " 状态: ", state_weno3 + print *, "" + + ! ========== WENO5 求解器 ========== + print *, "[STEP 4] 配置和运行 WENO5 求解器..." + print *, "-----------------------------------" + + config_weno5 = config_eno3 + call config_with_reconstruction(config_weno5, "weno", 5) + + solver_weno5%config = config_weno5 + solver_weno5%mesh = mesh + call solver_weno5%enable_real_data(.false.) + + call solver_weno5%initialize() + call solver_weno5%run_to_time(config_weno5%final_time) + + time_weno5 = solver_weno5%current_time + steps_weno5 = solver_weno5%current_step + state_weno5 = solver_weno5%get_state() + + print *, "WENO5 完成:" + print *, " 最终时间: ", time_weno5 + print *, " 总步数: ", steps_weno5 + print *, " 状态: ", state_weno5 + print *, "" + + ! ========== 结果汇总 ========== + print *, "==========================================" + print *, " 结果汇总" + print *, "==========================================" + print *, "" + + all_success = (state_eno3 == SOLVER_COMPLETED) .and. & + (state_weno3 == SOLVER_COMPLETED) .and. & + (state_weno5 == SOLVER_COMPLETED) + + if (all_success) then + print *, "✓ 所有求解器成功完成!" + print *, "" + print *, "性能对比:" + print *, " ENO3: ", steps_eno3, " 步" + print *, " WENO3: ", steps_weno3, " 步" + print *, " WENO5: ", steps_weno5, " 步" + print *, "" + print *, "网格信息:" + print *, " 单元数: ", mesh%ncells + print *, " 域长度: ", mesh%L + print *, " 网格间距: ", mesh%dx + print *, "" + print *, "数据模式: 简单数据 (一阶迎风格式)" + print *, "切换真实计算: 调用 solver%enable_real_data(.true.)" + else + print *, "✗ 部分求解器失败" + print *, " ENO3状态: ", state_eno3, " (期望: ", SOLVER_COMPLETED, ")" + print *, " WENO3状态: ", state_weno3, " (期望: ", SOLVER_COMPLETED, ")" + print *, " WENO5状态: ", state_weno5, " (期望: ", SOLVER_COMPLETED, ")" + end if + + print *, "" + print *, "==========================================" + print *, " 分析完成" + print *, "==========================================" + + ! 清理 + call solver_eno3%cleanup() + call solver_weno3%cleanup() + call solver_weno5%cleanup() + + ! 等待用户输入 + print *, "" + print *, "按 ENTER 键退出..." + read(*,*) + +end program run_eno_weno_integrated \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/python/plot_results.py b/example/1d-linear-convection/weno3/fortran/registry/03g/python/plot_results.py new file mode 100644 index 00000000..cf8e4017 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/python/plot_results.py @@ -0,0 +1,419 @@ +#!/usr/bin/env python3 +# python/plot_results.py +""" +Fortran CFD 结果可视化脚本 +与Julia的plotter.jl功能类似,但直接读取Fortran生成的文本文件 +""" + +import numpy as np +import matplotlib.pyplot as plt +import os +import sys +import glob +from pathlib import Path +import re + +class FortranResultsPlotter: + """Fortran结果绘图器""" + + def __init__(self, style='default'): + self.style = style + self.setup_styles() + + def setup_styles(self): + """设置绘图样式""" + if self.style == 'default': + self.styles = { + 'numerical': { + 'color': 'blue', + 'linestyle': '-', + 'marker': 'o', + 'markerfacecolor': 'none', + 'markersize': 4, + 'linewidth': 1 + }, + 'analytical': { + 'color': 'red', + 'linestyle': '--', + 'marker': '', + 'linewidth': 1.5 + }, + 'comparison': [ + {'color': 'black', 'linestyle': '-', 'marker': 'o', 'markerfacecolor': 'none'}, + {'color': 'blue', 'linestyle': '--', 'marker': 's', 'markerfacecolor': 'none'}, + {'color': 'green', 'linestyle': ':', 'marker': '^', 'markerfacecolor': 'none'} + ] + } + + def read_fortran_results(self, filename): + """读取Fortran生成的结果文件""" + data = {} + + try: + with open(filename, 'r', encoding='utf-8', errors='ignore') as f: + lines = f.readlines() + + data_lines = [] + in_data_section = False # 新状态:是否在数据区域 + + for line in lines: + line = line.strip() + if not line: + continue + + # 跳过所有分隔线 + if line.startswith("===="): + continue + + # 检测数据区域开始 + if line == "DATA: x, numerical, analytical": + in_data_section = True + continue + + if not in_data_section: + # 解析头部信息 + if "Solver:" in line: + data['solver'] = line.split(":", 1)[1].strip() + elif "Scheme:" in line: + data['scheme'] = line.split(":", 1)[1].strip() + elif "Order:" in line: + if "RK Order:" in line: + data['rk_order'] = int(line.split(":", 1)[1].strip()) + else: + data['order'] = int(line.split(":", 1)[1].strip()) + elif "Current Time:" in line: + data['time'] = float(line.split(":", 1)[1].strip()) + elif "Grid Points:" in line: + data['n_points'] = int(line.split(":", 1)[1].strip()) + else: + # 解析数据行 + parts = line.split() + if len(parts) >= 3: + try: + x = float(parts[0]) + numerical = float(parts[1]) + analytical = float(parts[2]) + data_lines.append([x, numerical, analytical]) + except ValueError: + continue # 忽略无法解析的行 + + if data_lines: + import numpy as np + data_array = np.array(data_lines) + data['x'] = data_array[:, 0] + data['numerical'] = data_array[:, 1] + data['analytical'] = data_array[:, 2] + + print(f"Read {len(data['x'])} points from {filename}") + print(f" Solver: {data.get('solver', 'N/A')}") + print(f" Scheme: {data.get('scheme', 'N/A')} order {data.get('order', 'N/A')}") + print(f" Time: {data.get('time', 'N/A')}") + + return data + else: + print(f"Warning: No data found in {filename}") + return None + + except Exception as e: + print(f"Error reading {filename}: {e}") + return None + + def plot_single_result(self, filename, title=None, show=True, save_path=None): + """绘制单个结果文件""" + data = self.read_fortran_results(filename) + if data is None: + return False + + fig, ax = plt.subplots(figsize=(10, 6)) + + # 自动生成标题 + if title is None: + title = f"1D Convection (t={data['time']:.3f})\n" + title += f"{data['order']}th-order {data['scheme'].upper()} + {data['rk_order']}nd-order RK" + + # 绘制数值解 + ax.plot(data['x'], data['numerical'], + label=f"Numerical ({data['scheme'].upper()}{data['order']})", + **self.styles['numerical']) + + # 绘制解析解 + ax.plot(data['x'], data['analytical'], + label="Analytical", + **self.styles['analytical']) + + # 设置图形属性 + ax.set_title(title, fontsize=12) + ax.set_xlabel("x", fontsize=10) + ax.set_ylabel("u", fontsize=10) + ax.legend(fontsize=9) + ax.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + + # 保存或显示 + if save_path: + plt.savefig(save_path, dpi=150, bbox_inches='tight') + print(f"Saved plot to {save_path}") + + if show: + plt.show() + else: + plt.close() + + return True + + def format_scheme_label(self, scheme: str, order: int) -> str: + s = scheme.upper() + if s.startswith("WENO"): + return f"WENO{order}" + else: + return f"{s}{order}" + + def plot_comparison(self, filenames, labels=None, title=None, show=True, save_path=None): + """比较多个结果文件""" + all_data = [] + print(f"plot_comparison filenames={filenames}") + + # 读取所有文件 + for filename in filenames: + print(f"plot_comparison filename={filename}") + data = self.read_fortran_results(filename) + if data is not None: + all_data.append(data) + + if not all_data: + print("No valid data to plot") + return False + + fig, axes = plt.subplots(2, 2, figsize=(14, 10)) + + # 子图1:所有方法比较 + for i, data in enumerate(all_data): + #print(f"i,all_data={i,all_data}") + if labels and i < len(labels): + label = labels[i] + else: + label = f"{data['scheme'].upper()}{data['order']}" + + style_idx = i % len(self.styles['comparison']) + style = self.styles['comparison'][style_idx] + + axes[0, 0].plot(data['x'], data['numerical'], + label=label, **style) + + axes[0, 0].plot(all_data[0]['x'], all_data[0]['analytical'], + label="Analytical", **self.styles['analytical']) + + axes[0, 0].set_xlabel('x', fontsize=12) + axes[0, 0].set_ylabel('u(x)', fontsize=12) + axes[0, 0].set_title('Numerical Solutions Comparison', fontsize=14) + axes[0, 0].legend() + axes[0, 0].grid(True, alpha=0.3) + + # 子图2:误差分析 + for i, data in enumerate(all_data): + if labels and i < len(labels): + label = labels[i] + else: + label = f"{data['scheme'].upper()}{data['order']}" + + error = np.abs(data['numerical'] - data['analytical']) + axes[0, 1].semilogy(data['x'], error, label=label) + + axes[0, 1].set_xlabel('x', fontsize=12) + axes[0, 1].set_ylabel('Absolute Error', fontsize=12) + axes[0, 1].set_title('Error Comparison', fontsize=14) + axes[0, 1].legend() + axes[0, 1].grid(True, alpha=0.3) + + # 子图3:数值解细节(放大) + x_min, x_max = all_data[0]['x'].min(), all_data[0]['x'].max() + zoom_center = 1.0 # 阶跃函数位置附近 + zoom_width = 0.3 + + for i, data in enumerate(all_data): + if labels and i < len(labels): + label = labels[i] + else: + label = f"{data['scheme'].upper()}{data['order']}" + + style_idx = i % len(self.styles['comparison']) + style = self.styles['comparison'][style_idx] + + axes[1, 0].plot(data['x'], data['numerical'], label=label, **style) + + axes[1, 0].plot(all_data[0]['x'], all_data[0]['analytical'], + label="Analytical", **self.styles['analytical']) + + axes[1, 0].set_xlim(zoom_center - zoom_width/2, zoom_center + zoom_width/2) + axes[1, 0].set_xlabel('x', fontsize=12) + axes[1, 0].set_ylabel('u(x)', fontsize=12) + axes[1, 0].set_title('Zoomed View (x ≈ 1.0)', fontsize=14) + axes[1, 0].legend() + axes[1, 0].grid(True, alpha=0.3) + + # 子图4:性能统计 + axes[1, 1].axis('off') + + # 计算并显示L2误差 + errors = [] + schemes = [] + + for data in all_data: + error = np.sqrt(np.mean((data['numerical'] - data['analytical'])**2)) + errors.append(error) + schemes.append(f"{data['scheme'].upper()}{data['order']}") + + # 创建表格数据 + table_data = [] + for i, (scheme, error) in enumerate(zip(schemes, errors)): + table_data.append([scheme, f"{error:.2e}"]) + + # 在子图中显示表格 + table = axes[1, 1].table(cellText=table_data, + colLabels=['Scheme', 'L2 Error'], + loc='center', + cellLoc='center', + colWidths=[0.3, 0.4]) + + table.auto_set_font_size(False) + table.set_fontsize(10) + table.scale(1, 1.5) + + axes[1, 1].set_title('Performance Summary (L2 Error)', fontsize=14) + + # 设置总标题 + if title is None: + time = all_data[0]['time'] + schemes_str = ", ".join([self.format_scheme_label(d['scheme'], d['order']) for d in all_data]) + title = f"1D Convection Comparison (t={time:.3f})\n{schemes_str}" + + fig.suptitle(title, fontsize=16) + plt.tight_layout() + + # 保存或显示 + if save_path: + plt.savefig(save_path, dpi=150, bbox_inches='tight') + print(f"Saved comparison plot to {save_path}") + + if show: + plt.show() + else: + plt.close() + + return True + + def get_scheme_from_filename(self, filename): + print(f"filename={filename}") + stem = Path(filename).stem # e.g., "results_ENO3_40" + parts = stem.split('_') + if len(parts) >= 2: + return parts[1] # "ENO3", "WENO3", "WENO5" + return "" + + def plot_eno_weno_comparison(self, result_dir=".", save_path="eno_weno_comparison.png"): + """自动绘制ENO/WENO对比图(类似Julia的功能)""" + # 查找结果文件 + pattern = os.path.join(result_dir, "results_*.dat") + files = glob.glob(pattern) + + if not files: + print(f"No result files found matching {pattern}") + return False + + # 重新分类 + file_info = [] + eno_files = [] + weno3_files = [] + weno5_files = [] + for f in files: + print(f"f={f}") + print(f"type(f)={type(f)}") + scheme = self.get_scheme_from_filename(f) + print(f"scheme={scheme}") + if scheme == "ENO3": + eno_files.append(f) + elif scheme == "WENO3": + weno3_files.append(f) + elif scheme == "WENO5": + weno5_files.append(f) + + # 按求解器类型分类 + print(f"eno_files={eno_files}") + print(f"weno3_files={weno3_files}") + print(f"weno5_files={weno5_files}") + + # 选择最新的文件(如果有多组) + files_to_plot = [] + labels = [] + + if eno_files: + files_to_plot.append(sorted(eno_files)[-1]) + labels.append("ENO3") + + if weno3_files: + files_to_plot.append(sorted(weno3_files)[-1]) + labels.append("WENO3") + + if weno5_files: + files_to_plot.append(sorted(weno5_files)[-1]) + labels.append("WENO5") + + if len(files_to_plot) >= 2: + print(f"Plotting comparison of {len(files_to_plot)} solvers") + return self.plot_comparison(files_to_plot, labels=labels, + save_path=save_path, show=True) + else: + print("Not enough different solvers for comparison") + return False + +def main(): + """主函数""" + import argparse + + parser = argparse.ArgumentParser(description='Fortran CFD Results Visualizer') + parser.add_argument('--file', help='Plot single result file') + parser.add_argument('--compare', nargs='+', help='Compare multiple files') + parser.add_argument('--dir', default='.', help='Directory containing result files') + parser.add_argument('--auto', action='store_true', help='Auto plot ENO/WENO comparison') + parser.add_argument('--save', help='Save plot to file (without showing)') + parser.add_argument('--no-show', action='store_true', help='Don\'t show plot') + + args = parser.parse_args() + + plotter = FortranResultsPlotter() + + if args.file: + # 绘制单个文件 + plotter.plot_single_result(args.file, save_path=args.save, + show=not args.no_show) + + elif args.compare: + # 比较多个文件 + plotter.plot_comparison(args.compare, save_path=args.save, + show=not args.no_show) + + elif args.auto: + # 自动绘制比较图 + save_path = args.save or "eno_weno_comparison.png" + plotter.plot_eno_weno_comparison(args.dir, save_path=save_path) + + else: + # 默认:显示帮助 + parser.print_help() + + # 也显示可用的结果文件 + print("\nAvailable result files:") + pattern = os.path.join(args.dir, "results_*.dat") + files = glob.glob(pattern) + + if files: + for f in sorted(files): + print(f" {os.path.basename(f)}") + + print(f"\nTo plot ENO/WENO comparison automatically:") + print(f" python plot_results.py --auto") + else: + print(" No result files found") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/build.bat b/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/build.bat new file mode 100644 index 00000000..6fd6dc03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/build.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Project Builder +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python build script with full Intel environment support... +echo. + +python build.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Build failed + pause + exit /b 1 +) + +echo. +echo [INFO] Build completed successfully! +echo. +echo [INFO] To run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/build.py b/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/build.py new file mode 100644 index 00000000..3bf6d537 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/build.py @@ -0,0 +1,629 @@ +#!/usr/bin/env python3 +""" +Fortran CFD Project Builder - 完整Python解决方案 +在Python内部处理Intel oneAPI环境配置 +""" + +import os +import sys +import subprocess +import shutil +import argparse +import time +import platform +import tempfile +from pathlib import Path + +class IntelEnvironment: + """Intel oneAPI环境管理器""" + + def __init__(self): + self.setvars_path = None + self.env_vars = {} + + def find_setvars(self): + """查找setvars.bat文件""" + possible_paths = [ + r"C:\Program Files (x86)\Intel\oneAPI\setvars.bat", + r"C:\Program Files\Intel\oneAPI\setvars.bat", + r"C:\Program Files (x86)\Intel\oneAPI\compiler\latest\env\vars.bat", + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\setvars.bat"), + os.path.expandvars(r"%INTEL_ONEAPI_ROOT%\compiler\latest\env\vars.bat"), + ] + + for path in possible_paths: + if os.path.exists(path): + self.setvars_path = path + return True + + return False + + def setup_environment(self): + """设置Intel环境""" + if not self.find_setvars(): + return False + + try: + # 创建临时的批处理文件来捕获环境变量 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + f.write(f'@echo off\n') + f.write(f'call "{self.setvars_path}" >nul 2>&1\n') + f.write(f'set\n') # 输出所有环境变量 + temp_bat = f.name + + # 运行批处理文件并捕获输出 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + shell=True + ) + + # 解析环境变量 + for line in result.stdout.split('\n'): + line = line.strip() + if '=' in line: + key, value = line.split('=', 1) + self.env_vars[key.strip()] = value.strip() + + # 清理临时文件 + os.unlink(temp_bat) + + # 更新当前进程的环境变量 + os.environ.update(self.env_vars) + + return True + + except Exception as e: + print(f"设置Intel环境失败: {e}") + return False + + def get_compiler_info(self): + """获取编译器信息""" + info = {} + + # 检查ifx编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore', + env={**os.environ, **self.env_vars} if self.env_vars else os.environ + ) + + if result.returncode == 0: + for line in result.stdout.split('\n'): + if 'Version' in line or '版本' in line: + info['ifx_version'] = line.strip() + break + except: + pass + + # 检查环境变量 + info['ifx_root'] = self.env_vars.get('IFX_ROOT', '') + info['compiler_root'] = self.env_vars.get('ONEAPI_ROOT', '') + + return info + +class BuildSystem: + """构建系统主类""" + + def __init__(self): + self.project_root = Path(__file__).parent.parent + self.build_dir = self.project_root / "build" + self.intel_env = IntelEnvironment() + + # 设置控制台编码 + if sys.platform == "win32": + try: + import ctypes + # 设置控制台输出为UTF-8 + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + except: + pass + + def print_header(self, text): + """打印标题""" + print(f"\n{'='*70}") + print(f" {text}") + print(f"{'='*70}\n") + + def print_step(self, step, total, message): + """打印步骤""" + print(f"[{step}/{total}] {message}...") + + def print_success(self, message): + """打印成功""" + print(f"\033[92m✓ {message}\033[0m") + + def print_error(self, message): + """打印错误""" + print(f"\033[91m✗ {message}\033[0m") + + def print_warning(self, message): + """打印警告""" + print(f"\033[93m! {message}\033[0m") + + def print_info(self, message): + """打印信息""" + print(f"\033[94mℹ {message}\033[0m") + + def check_prerequisites(self): + """检查前提条件""" + self.print_step(1, 6, "检查前提条件") + + # 检查Python版本 + python_version = sys.version.split()[0] + self.print_info(f"Python版本: {python_version}") + + # 检查平台 + self.print_info(f"平台: {platform.system()} {platform.release()}") + self.print_info(f"处理器核心数: {os.cpu_count()}") + + # 检查CMake + try: + result = subprocess.run( + ["cmake", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + version_line = result.stdout.split('\n')[0] + self.print_success(f"CMake: {version_line}") + else: + self.print_error("CMake未找到") + return False + except FileNotFoundError: + self.print_error("CMake未安装") + return False + + return True + + def setup_intel_environment(self, args): + """设置Intel环境""" + self.print_step(2, 6, "配置Intel oneAPI环境") + + if not self.intel_env.find_setvars(): + self.print_warning("未找到Intel oneAPI setvars.bat") + self.print_info("将尝试使用系统环境中的编译器") + + # 检查是否能直接访问编译器 + try: + result = subprocess.run( + ["ifx", "--version"], + capture_output=True, + text=True, + encoding='utf-8', + errors='ignore' + ) + if result.returncode == 0: + self.print_success("Intel编译器在系统PATH中找到") + return True + else: + self.print_warning("Intel编译器未在PATH中找到") + except: + self.print_warning("无法访问Intel编译器") + + return True # 继续,让CMake自己找编译器 + + # 设置环境 + if self.intel_env.setup_environment(): + compiler_info = self.intel_env.get_compiler_info() + + if compiler_info.get('ifx_version'): + self.print_success(f"Intel Fortran编译器: {compiler_info['ifx_version']}") + elif compiler_info.get('ifx_root'): + self.print_success(f"Intel编译器路径: {compiler_info['ifx_root']}") + else: + self.print_success("Intel oneAPI环境配置完成") + + return True + else: + self.print_warning("Intel环境配置失败,将继续使用系统环境") + return True + + def clean_build_directory(self, args): + """清理构建目录""" + if args.clean and self.build_dir.exists(): + self.print_info("清理构建目录...") + try: + shutil.rmtree(self.build_dir) + self.print_success("构建目录已清理") + except Exception as e: + self.print_error(f"清理失败: {e}") + if not args.force: + return False + return True + + def run_command(self, cmd, cwd=None, check=True, env=None): + """运行命令""" + if isinstance(cmd, list): + cmd_str = ' '.join(str(c) for c in cmd if c) + else: + cmd_str = str(cmd) + + print(f" \033[96m$\033[0m {cmd_str}") + + try: + # 合并环境变量 + exec_env = os.environ.copy() + if env: + exec_env.update(env) + if self.intel_env.env_vars: + exec_env.update(self.intel_env.env_vars) + + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=False, + env=exec_env + ) + + # 处理输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or '完成' in line or '生成' in line: + print(f" \033[92m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + if check and result.returncode != 0: + self.print_error(f"命令执行失败,退出码: {result.returncode}") + return False + + return True + + except Exception as e: + self.print_error(f"命令执行异常: {e}") + return False + + def configure_cmake(self, args): + """配置CMake""" + self.print_step(3, 6, "配置CMake项目") + + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + f"-DCMAKE_BUILD_TYPE={args.build_type}", + ] + + if args.compiler == "ifx": + cmake_cmd.extend(["-T", "fortran=ifx"]) + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + success = self.run_command(cmake_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("CMake配置完成") + else: + self.print_error("CMake配置失败") + + return success + + def build_project(self, args): + """构建项目""" + self.print_step(4, 6, "构建项目") + + build_cmd = [ + "cmake", + "--build", ".", + "--config", args.build_type, + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + success = self.run_command(build_cmd, cwd=self.build_dir, check=True) + + if success: + self.print_success("项目构建完成") + else: + self.print_error("构建失败") + + return success + + def run_tests_with_environment(self, test_exe): + """运行单个测试,确保有Intel环境""" + try: + # 创建临时的批处理文件来运行测试 + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'"{test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'"{test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + return result + + except Exception as e: + print(f"运行测试失败: {e}") + return None + + def run_tests(self, args): + """运行测试""" + self.print_step(5, 6, "运行测试") + + # 查找测试可执行文件 + test_dir = self.build_dir / "bin" / args.build_type + if not test_dir.exists(): + test_dir = self.build_dir / "bin" + if not test_dir.exists(): + test_dir = self.build_dir + + test_files = list(test_dir.glob("test_*.exe")) + + if not test_files: + self.print_warning("未找到测试程序") + return True + + all_passed = True + + for test_exe in sorted(test_files): + test_name = test_exe.stem + self.print_info(f"运行测试: {test_name}") + print(f" {'-'*50}") + + # 运行测试 + result = self.run_tests_with_environment(str(test_exe)) + + if result is None: + self.print_error(f" {test_name} 运行失败") + all_passed = False + continue + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + print(f" {line}") + + if result.returncode == 0: + self.print_success(f" {test_name} 通过") + else: + self.print_error(f" {test_name} 失败 (退出码: {result.returncode})") + all_passed = False + + if result.stderr: + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + print() # 空行 + + return all_passed + + def create_test_runner(self, args): + """创建独立的测试运行器""" + self.print_step(6, 6, "创建测试运行器") + + runner_path = self.build_dir / "run_tests.bat" + + content = f'''@echo off +chcp 65001 >nul + +echo ======================================== +echo Fortran CFD Test Runner +echo ======================================== +echo. + +REM Setup Intel oneAPI environment +set "SETVARS_PATH={self.intel_env.setvars_path or ''}" +if exist "%SETVARS_PATH%" ( + call "%SETVARS_PATH%" >nul + echo [INFO] Intel environment configured +) else ( + echo [WARNING] Intel environment not found + echo [WARNING] Tests may fail without runtime libraries +) + +echo. + +REM Run all test executables +set "TEST_COUNT=0" +set "PASS_COUNT=0" + +for %%f in ("bin\\{args.build_type}\\test_*.exe") do ( + set /a TEST_COUNT+=1 + echo [TEST %%f] + echo {'-'*50} + + %%f + if errorlevel 1 ( + echo [FAILED] %%f + ) else ( + echo [PASSED] %%f + set /a PASS_COUNT+=1 + ) + echo. +) + +echo ======================================== +echo Tests: %PASS_COUNT%/%TEST_COUNT% passed +if %PASS_COUNT% equ %TEST_COUNT% ( + echo [SUCCESS] All tests passed! +) else ( + echo [FAILURE] Some tests failed +) +echo ======================================== + +pause +''' + + with open(runner_path, 'w', encoding='utf-8') as f: + f.write(content) + + self.print_success(f"测试运行器已创建: {runner_path}") + self.print_info(f"使用方法: cd build && run_tests.bat") + + return runner_path + + def generate_report(self, args, build_time, tests_passed): + """生成构建报告""" + self.print_header("构建完成") + + print(f"项目: {self.project_root.name}") + print(f"构建类型: {args.build_type}") + print(f"编译器: {args.compiler}") + print(f"并行作业: {args.jobs}") + print(f"总耗时: {build_time:.1f}秒") + print(f"测试结果: {'全部通过' if tests_passed else '有失败'}") + + # 显示生成的可执行文件 + bin_dir = self.build_dir / "bin" / args.build_type + if bin_dir.exists(): + print(f"\n生成的可执行文件:") + for exe in sorted(bin_dir.glob("*.exe")): + size_mb = exe.stat().st_size / (1024 * 1024) + print(f" • {exe.name} ({size_mb:.2f} MB)") + + # 显示测试运行器信息 + runner_path = self.build_dir / "run_tests.bat" + if runner_path.exists(): + print(f"\n独立测试运行器:") + print(f" • {runner_path.name}") + print(f" 在Intel oneAPI环境中运行所有测试") + + def run(self): + """运行构建系统""" + parser = argparse.ArgumentParser( + description="Fortran CFD项目构建工具", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认构建 + %(prog)s --clean # 清理后构建 + %(prog)s --build-type Release # Release构建 + %(prog)s --no-tests # 只构建,不运行测试 + %(prog)s -j8 --verbose # 8线程并行构建,详细输出 + """ + ) + + parser.add_argument("--build-type", choices=["Debug", "Release"], + default="Debug", help="构建类型") + parser.add_argument("--compiler", choices=["ifx", "ifort"], + default="ifx", help="Fortran编译器") + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-tests", action="store_true", + help="跳过测试") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + + args = parser.parse_args() + + # 开始构建 + start_time = time.time() + + self.print_header("Fortran CFD 项目构建系统") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 检查前提条件 + if not self.check_prerequisites(): + if not args.force: + return 1 + + # 2. 设置Intel环境 + if not self.setup_intel_environment(args): + if not args.force: + return 1 + + # 3. 清理目录 + if not self.clean_build_directory(args): + if not args.force: + return 1 + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 4. 配置CMake + if not self.configure_cmake(args): + if not args.force: + return 1 + + # 5. 构建项目 + if not self.build_project(args): + if not args.force: + return 1 + + # 6. 运行测试和创建测试运行器 + tests_passed = True + if not args.no_tests: + tests_passed = self.run_tests(args) + + # 创建测试运行器 + self.create_test_runner(args) # 传递 args 参数 + + # 7. 生成报告 + build_time = time.time() - start_time + self.generate_report(args, build_time, tests_passed) + + return 0 if tests_passed else 1 + + except KeyboardInterrupt: + self.print_error("\n构建被用户中断") + return 1 + except Exception as e: + self.print_error(f"构建过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + builder = BuildSystem() + return builder.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/requirements.txt b/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/requirements.txt new file mode 100644 index 00000000..d21a12b4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/requirements.txt @@ -0,0 +1,2 @@ +# 构建脚本的Python依赖(可选) +# 目前没有特殊依赖,保持空文件或添加未来可能需要的包 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/run_all_steps.bat b/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/run_all_steps.bat new file mode 100644 index 00000000..d506149b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/run_all_steps.bat @@ -0,0 +1,38 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo CFD Project: All Steps +echo ======================================== +echo. + +echo [INFO] Starting Step 1: Physics Modules Test... +call run_step1.bat + +if errorlevel 1 ( + echo [ERROR] Step 1 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Starting Step 2: Configuration Physics Update... +call run_step2.bat + +if errorlevel 1 ( + echo [ERROR] Step 2 failed + pause + exit /b 1 +) + +echo. +echo ======================================== +echo All Steps Completed Successfully! +echo ======================================== +echo. +echo [INFO] Next: Update component manager for physics support +echo [INFO] Run: run_step3.bat (to be created) +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/run_example.py b/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/run_example.py new file mode 100644 index 00000000..d7c19917 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/run_example.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +# scripts/run_example.py +""" +运行ENO/WENO示例程序 +""" + +import os +import sys +import subprocess +from pathlib import Path + +# 添加当前目录到路径 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import BuildSystem +except ImportError: + print("错误: 找不到build.py,请确保在scripts目录中运行") + sys.exit(1) + +def run_example(): + """运行示例程序""" + builder = BuildSystem() + + print("\n" + "="*70) + print(" 运行ENO/WENO对比示例程序") + print("="*70 + "\n") + + # 检查是否已构建 + exe_path = builder.build_dir / "bin" / "Debug" / "example_eno_weno_comparison.exe" + + if not exe_path.exists(): + print("示例程序未构建,先构建项目...") + print("-"*50) + + # 使用简化的构建 + result = subprocess.run( + ["python", "build.py", "--no-tests", "--clean"], + cwd=builder.project_root / "scripts", + capture_output=True, + text=True + ) + + if result.returncode != 0: + print("构建失败:") + print(result.stderr) + return False + + # 运行示例程序 + print("运行示例程序...") + print("-"*50) + + try: + result = subprocess.run( + [str(exe_path)], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace' + ) + + print(result.stdout) + + if result.stderr: + print("标准错误输出:") + print(result.stderr) + + return result.returncode == 0 + + except Exception as e: + print(f"运行示例程序失败: {e}") + return False + +def main(): + """主函数""" + success = run_example() + + if success: + print("\n" + "="*70) + print(" ✓ 示例程序运行成功") + print("="*70) + return 0 + else: + print("\n" + "="*70) + print(" ✗ 示例程序运行失败") + print("="*70) + return 1 + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/run_step1.bat b/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/run_step1.bat new file mode 100644 index 00000000..0b6b1f17 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/run_step1.bat @@ -0,0 +1,39 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Step 1: Physics Modules Test +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python step1 script with full Intel environment support... +echo. + +python run_step1.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Step 1 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Step 1 completed successfully! +echo. +echo [INFO] Next step: Update config to include physics settings +echo [INFO] Run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/run_step1.py b/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/run_step1.py new file mode 100644 index 00000000..5e087a69 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/run_step1.py @@ -0,0 +1,319 @@ +#!/usr/bin/env python3 +""" +Step 1: Physics Modules Test +扩展build.py,专门用于测试物理模块 +""" + +import os +import sys +import subprocess +import time +from pathlib import Path + +# 添加当前目录到路径,以便导入build.py的类 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import IntelEnvironment, BuildSystem +except ImportError: + print("Error: Cannot import build.py. Make sure build.py is in the same directory.") + sys.exit(1) + +class Step1System(BuildSystem): + """Step 1 测试系统,继承自BuildSystem""" + + def __init__(self): + super().__init__() + self.test_name = "test_physics_minimal" + self.test_exe = None + + def find_test_executable(self): + """查找测试可执行文件""" + possible_paths = [ + self.build_dir / "bin" / "Debug" / f"{self.test_name}.exe", + self.build_dir / "Debug" / f"{self.test_name}.exe", + self.build_dir / f"{self.test_name}.exe", + self.build_dir / "bin" / f"{self.test_name}.exe", + ] + + for path in possible_paths: + if path.exists(): + self.test_exe = path + self.print_success(f"Found test executable: {path}") + return True + + # 如果没有找到,尝试搜索 + self.print_warning(f"Could not find {self.test_name}.exe") + self.print_info("Searching for test executables...") + + try: + result = subprocess.run( + ["dir", str(self.build_dir), "/s", "/b", "*.exe"], + capture_output=True, + text=True, + encoding='utf-8', + shell=True + ) + + if result.returncode == 0: + test_files = [line.strip() for line in result.stdout.split('\n') + if line and 'test_' in line.lower()] + + if test_files: + self.print_info("Found test files:") + for test_file in test_files: + self.print_info(f" {test_file}") + return False + except: + pass + + return False + + def run_test_with_intel_env(self): + """在Intel环境下运行测试""" + if not self.test_exe: + self.print_error("No test executable found") + return False + + self.print_step(1, 2, f"Running test: {self.test_exe.name}") + + try: + # 创建临时的批处理文件来运行测试(包含Intel环境) + import tempfile + + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + # 设置Intel环境 + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'echo [INFO] Intel environment configured\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'echo [WARNING] Intel environment not found\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + self.print_info(f"Command: {temp_bat}") + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower() or 'fail' in line.lower(): + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or 'pass' in line.lower() or '✓' in line: + print(f" \033[92m{line}\033[0m") + elif '=' in line or '---' in line: + print(f" \033[96m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + self.print_warning("Test stderr output:") + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + self.print_step(2, 2, "Test execution completed") + + if result.returncode == 0: + self.print_success("Test passed") + return True + else: + self.print_error(f"Test failed (exit code: {result.returncode})") + return False + + except Exception as e: + self.print_error(f"Failed to run test: {e}") + return False + + def build_project_if_needed(self, args): + """如果需要,构建项目""" + if args.no_build: + self.print_info("Skipping build (--no-build flag)") + return True + + self.print_step(1, 3, "Building project") + + # 调用父类的构建方法 + build_args = argparse.Namespace() + build_args.clean = args.clean + build_args.build_type = "Debug" + build_args.compiler = "ifx" + build_args.no_tests = True # 不运行所有测试 + build_args.jobs = os.cpu_count() + build_args.verbose = args.verbose + build_args.force = args.force + + # 清理构建目录 + if args.clean and self.build_dir.exists(): + self.print_info("Cleaning build directory...") + import shutil + try: + shutil.rmtree(self.build_dir) + self.print_success("Build directory cleaned") + except Exception as e: + self.print_error(f"Clean failed: {e}") + if not args.force: + return False + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 配置CMake + self.print_step(2, 3, "Configuring CMake") + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-DCMAKE_BUILD_TYPE=Debug", + "-T", "fortran=ifx", + ] + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + if not self.run_command(cmake_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + # 构建项目 + self.print_step(3, 3, "Building project") + build_cmd = [ + "cmake", + "--build", ".", + "--config", "Debug", + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + if not self.run_command(build_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + self.print_success("Build completed") + return True + + def run(self): + """运行Step 1测试""" + parser = argparse.ArgumentParser( + description="Step 1: Physics Modules Test", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认运行 + %(prog)s --clean # 清理后构建并测试 + %(prog)s --no-build # 只运行测试,不重新构建 + %(prog)s --verbose # 详细输出 + %(prog)s -j4 # 使用4个并行作业构建 + """ + ) + + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-build", action="store_true", + help="不重新构建,直接运行测试") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + + args = parser.parse_args() + + # 开始测试 + start_time = time.time() + + self.print_header("Step 1: Physics Modules Implementation Test") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 设置Intel环境 + self.print_step(1, 4, "Setting up Intel oneAPI environment") + if not self.setup_intel_environment(args): + if not args.force: + self.print_error("Intel environment setup failed") + return 1 + self.print_warning("Intel environment setup failed, continuing...") + + # 2. 构建项目(如果需要) + self.print_step(2, 4, "Building project if needed") + if not self.build_project_if_needed(args): + if not args.force: + return 1 + + # 3. 查找测试可执行文件 + self.print_step(3, 4, "Finding test executable") + if not self.find_test_executable(): + if not args.force: + return 1 + self.print_warning("Test executable not found, but continuing due to --force") + return 0 + + # 4. 运行测试 + self.print_step(4, 4, "Running physics module test") + test_passed = self.run_test_with_intel_env() + + # 生成报告 + test_time = time.time() - start_time + self.print_header("Step 1 Complete") + + print(f"测试: {'通过 ✓' if test_passed else '失败 ✗'}") + print(f"测试程序: {self.test_exe.name if self.test_exe else '未找到'}") + print(f"总耗时: {test_time:.1f}秒") + + if test_passed: + print(f"\n下一步: 更新配置以包含物理设置") + print(f"建议: 修改config.f90,添加physics相关字段") + return 0 + else: + if args.force: + self.print_warning("测试失败,但由于--force标志继续执行") + return 0 + return 1 + + except KeyboardInterrupt: + self.print_error("\n测试被用户中断") + return 1 + except Exception as e: + self.print_error(f"测试过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + system = Step1System() + return system.run() + +if __name__ == "__main__": + # 需要导入argparse + import argparse + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/run_step2.bat b/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/run_step2.bat new file mode 100644 index 00000000..9c1f62de --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/run_step2.bat @@ -0,0 +1,39 @@ +@echo off +chcp 65001 >nul + +echo ======================================== +echo Step 2: Configuration Physics Update +echo (Python-based with Intel environment) +echo ======================================== +echo. + +python --version >nul 2>&1 +if errorlevel 1 ( + echo [ERROR] Python not found or not in PATH + pause + exit /b 1 +) + +echo [INFO] Running Python step2 script with full Intel environment support... +echo. + +python run_step2.py %* + +if errorlevel 1 ( + echo. + echo [ERROR] Step 2 failed + pause + exit /b 1 +) + +echo. +echo [INFO] Step 2 completed successfully! +echo. +echo [INFO] Next step: Update component manager to support physics +echo [INFO] Run tests independently: +echo cd build +echo run_tests.bat +echo. + +pause +exit /b 0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/run_step2.py b/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/run_step2.py new file mode 100644 index 00000000..c16b7608 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/run_step2.py @@ -0,0 +1,284 @@ +#!/usr/bin/env python3 +""" +Step 2: Configuration Physics Update +测试配置模块的物理功能更新 +""" + +import os +import sys +import subprocess +import time +from pathlib import Path + +# 添加当前目录到路径,以便导入build.py的类 +sys.path.insert(0, str(Path(__file__).parent)) + +try: + from build import IntelEnvironment, BuildSystem +except ImportError: + print("Error: Cannot import build.py. Make sure build.py is in the same directory.") + sys.exit(1) + +class Step2System(BuildSystem): + """Step 2 测试系统,继承自BuildSystem""" + + def __init__(self): + super().__init__() + self.test_name = "test_config_physics" + self.test_exe = None + + def find_test_executable(self): + """查找测试可执行文件""" + possible_paths = [ + self.build_dir / "bin" / "Debug" / f"{self.test_name}.exe", + self.build_dir / "Debug" / f"{self.test_name}.exe", + self.build_dir / f"{self.test_name}.exe", + self.build_dir / "bin" / f"{self.test_name}.exe", + ] + + for path in possible_paths: + if path.exists(): + self.test_exe = path + self.print_success(f"Found test executable: {path}") + return True + + return False + + def run_test_with_intel_env(self): + """在Intel环境下运行测试""" + if not self.test_exe: + self.print_error("No test executable found") + return False + + self.print_step(1, 2, f"Running test: {self.test_exe.name}") + + try: + # 创建临时的批处理文件来运行测试(包含Intel环境) + import tempfile + + with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f: + # 设置Intel环境 + if self.intel_env.setvars_path: + f.write(f'@echo off\n') + f.write(f'call "{self.intel_env.setvars_path}" >nul 2>&1\n') + f.write(f'echo [INFO] Intel environment configured\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + else: + f.write(f'@echo off\n') + f.write(f'echo [WARNING] Intel environment not found\n') + f.write(f'echo Running test: {self.test_exe.name}\n') + f.write(f'echo {"-"*50}\n') + f.write(f'"{self.test_exe}"\n') + + temp_bat = f.name + + # 运行测试 + self.print_info(f"Command: {temp_bat}") + result = subprocess.run( + [temp_bat], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + shell=True + ) + + # 清理临时文件 + os.unlink(temp_bat) + + # 显示输出 + if result.stdout: + for line in result.stdout.split('\n'): + line = line.strip() + if line: + # 高亮重要信息 + if 'error' in line.lower() or 'fail' in line.lower() or '✗' in line: + print(f" \033[91m{line}\033[0m") + elif 'warning' in line.lower(): + print(f" \033[93m{line}\033[0m") + elif 'success' in line.lower() or 'pass' in line.lower() or '✓' in line: + print(f" \033[92m{line}\033[0m") + elif '=' in line or '---' in line or '===' in line: + print(f" \033[96m{line}\033[0m") + else: + print(f" {line}") + + if result.stderr: + self.print_warning("Test stderr output:") + for line in result.stderr.split('\n'): + line = line.strip() + if line: + print(f" \033[93m{line}\033[0m") + + self.print_step(2, 2, "Test execution completed") + + if result.returncode == 0: + self.print_success("Test passed") + return True + else: + self.print_error(f"Test failed (exit code: {result.returncode})") + return False + + except Exception as e: + self.print_error(f"Failed to run test: {e}") + return False + + def build_project(self, args): + """构建项目""" + if args.no_build: + self.print_info("Skipping build (--no-build flag)") + return True + + self.print_step(1, 3, "Building project") + + # 清理构建目录 + if args.clean and self.build_dir.exists(): + self.print_info("Cleaning build directory...") + import shutil + try: + shutil.rmtree(self.build_dir) + self.print_success("Build directory cleaned") + except Exception as e: + self.print_error(f"Clean failed: {e}") + if not args.force: + return False + + # 确保构建目录存在 + self.build_dir.mkdir(exist_ok=True) + + # 配置CMake + self.print_step(2, 3, "Configuring CMake") + cmake_cmd = [ + "cmake", + "..", + "-G", "Visual Studio 17 2022", + "-A", "x64", + "-DCMAKE_BUILD_TYPE=Debug", + "-T", "fortran=ifx", + ] + + if args.verbose: + cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE=ON") + + if not self.run_command(cmake_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + # 构建项目 + self.print_step(3, 3, "Building project") + build_cmd = [ + "cmake", + "--build", ".", + "--config", "Debug", + ] + + if args.jobs > 1: + build_cmd.append(f"-j{args.jobs}") + + if not self.run_command(build_cmd, cwd=self.build_dir, check=not args.force): + if not args.force: + return False + + self.print_success("Build completed") + return True + + def run(self): + """运行Step 2测试""" + import argparse + + parser = argparse.ArgumentParser( + description="Step 2: Configuration Physics Update", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + %(prog)s # 默认运行 + %(prog)s --clean # 清理后构建并测试 + %(prog)s --no-build # 只运行测试,不重新构建 + %(prog)s --verbose # 详细输出 + """ + ) + + parser.add_argument("--clean", action="store_true", + help="构建前清理build目录") + parser.add_argument("--no-build", action="store_true", + help="不重新构建,直接运行测试") + parser.add_argument("--verbose", action="store_true", + help="详细输出") + parser.add_argument("--force", action="store_true", + help="出错时继续执行") + parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count(), + help="并行作业数") + + args = parser.parse_args() + + # 开始测试 + start_time = time.time() + + self.print_header("Step 2: Configuration Physics Update") + self.print_info(f"项目根目录: {self.project_root}") + + try: + # 1. 设置Intel环境 + self.print_step(1, 4, "Setting up Intel oneAPI environment") + if not self.setup_intel_environment(args): + if not args.force: + self.print_error("Intel environment setup failed") + return 1 + self.print_warning("Intel environment setup failed, continuing...") + + # 2. 构建项目(如果需要) + self.print_step(2, 4, "Building project if needed") + if not self.build_project(args): + if not args.force: + return 1 + + # 3. 查找测试可执行文件 + self.print_step(3, 4, "Finding test executable") + if not self.find_test_executable(): + self.print_error(f"Test executable {self.test_name}.exe not found") + if not args.force: + return 1 + self.print_warning("Test executable not found, but continuing due to --force") + return 0 + + # 4. 运行测试 + self.print_step(4, 4, "Running configuration physics test") + test_passed = self.run_test_with_intel_env() + + # 生成报告 + test_time = time.time() - start_time + self.print_header("Step 2 Complete") + + print(f"测试: {'通过 ✓' if test_passed else '失败 ✗'}") + print(f"测试程序: {self.test_exe.name if self.test_exe else '未找到'}") + print(f"总耗时: {test_time:.1f}秒") + + if test_passed: + print(f"\n下一步: 更新组件管理器以支持物理模块") + print(f"建议: 修改component_manager.f90,添加physics组件创建") + return 0 + else: + if args.force: + self.print_warning("测试失败,但由于--flag继续执行") + return 0 + return 1 + + except KeyboardInterrupt: + self.print_error("\n测试被用户中断") + return 1 + except Exception as e: + self.print_error(f"测试过程中出现未预期错误: {e}") + if args.verbose: + import traceback + traceback.print_exc() + return 1 + +def main(): + """主函数""" + system = Step2System() + return system.run() + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/test_integrated.py b/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/test_integrated.py new file mode 100644 index 00000000..91020a94 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/scripts/test_integrated.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# scripts/test_integrated.py +""" +测试集成求解器 +""" +import subprocess +import sys +import os +from pathlib import Path + +def run_test(): + """运行集成测试""" + # 构建项目 + print("构建项目...") + result = subprocess.run( + ["python", "scripts/build.py", "--no-tests", "--clean"], + capture_output=True, + text=True + ) + + if result.returncode != 0: + print("构建失败:") + print(result.stderr) + return False + + # 运行集成示例 + print("\n运行集成示例...") + exe_path = Path("build/bin/Debug/run_eno_weno_integrated.exe") + + if not exe_path.exists(): + print(f"可执行文件不存在: {exe_path}") + return False + + result = subprocess.run( + [str(exe_path)], + capture_output=True, + text=True, + encoding='utf-8' + ) + + print(result.stdout) + if result.stderr: + print("错误输出:") + print(result.stderr) + + return result.returncode == 0 + +if __name__ == "__main__": + success = run_test() + sys.exit(0 if success else 1) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03g/src/CMakeLists.txt new file mode 100644 index 00000000..ef816d13 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/CMakeLists.txt @@ -0,0 +1,41 @@ +# 修改后的完整版本: +message(STATUS "配置源代码目录...") + +# 设置包含目录 +include_directories(${CMAKE_Fortran_MODULE_DIRECTORY}) + +# 按依赖顺序添加子目录 +add_subdirectory(base) # 1. 基础类型和精度 +add_subdirectory(core) # 2. 核心注册系统 +add_subdirectory(infrastructure) # 3. 基础设施(配置、网格、域、解) +add_subdirectory(physics) # 4. 物理模块(方程和问题) +add_subdirectory(numerics) # 5. 数值方法(重构器、通量、时间积分) +add_subdirectory(manager) # 6. 组件管理器和工厂 +add_subdirectory(solver) # 7. 求解器 + +# ==================== 新增:边界条件和初始条件模块 ==================== +message(STATUS "配置边界条件模块...") +add_subdirectory(boundary) + +message(STATUS "配置初始条件模块...") +add_subdirectory(initial_condition) + +# ==================== 新增:结果模块 ==================== +message(STATUS "配置结果模块...") + +add_library(results STATIC + results.f90 # 新增文件 +) + +target_link_libraries(results + PRIVATE + base + infrastructure + solver +) + +set_target_properties(results PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "集成求解器库配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/base/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03g/src/base/CMakeLists.txt new file mode 100644 index 00000000..74f4aa65 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/base/CMakeLists.txt @@ -0,0 +1,16 @@ +# src/base/CMakeLists.txt +message(STATUS "Configuring base module...") + +add_library(base STATIC + modules.f90 + precision.f90 # 新增 +) + +set_target_properties(base PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Base module configured") + +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/base/modules.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/base/modules.f90 new file mode 100644 index 00000000..43aaee24 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/base/modules.f90 @@ -0,0 +1,36 @@ +! src/base/modules.f90 +module base_modules + use, intrinsic :: iso_fortran_env, only: real64, int32 + implicit none + + public :: wp, ip, max_name_len, string_len, cfd_config_base, component_info + + integer, parameter :: wp = real64 + integer, parameter :: ip = int32 + integer, parameter :: string_len = 100 + integer, parameter :: max_name_len = 32 + + ! 基础配置类型 + type :: cfd_config_base + character(len=max_name_len) :: ic_type = "step" + character(len=max_name_len) :: recon_scheme = "eno" + character(len=max_name_len) :: flux_type = "rusanov" + integer(ip) :: rk_order = 1 + real(wp) :: wave_speed = 1.0_wp + real(wp) :: final_time = 0.625_wp + real(wp) :: dt = 0.025_wp + character(len=max_name_len) :: boundary_type = "periodic" + integer(ip) :: spatial_order = 2 + character(len=max_name_len) :: equation_type = "linear_advection" + character(len=max_name_len) :: problem_type = "linear_advection" + logical :: verbose = .true. + end type cfd_config_base + + ! 组件信息类型 + type :: component_info + character(len=max_name_len) :: category = "" + character(len=max_name_len) :: name = "" + integer(ip) :: order = 0 + end type component_info + +end module base_modules \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/base/precision.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/base/precision.f90 new file mode 100644 index 00000000..4ac5fd7e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/base/precision.f90 @@ -0,0 +1,9 @@ +! src/base/precision.f90(简单版本) +module precision_module + use base_modules, only: wp, ip + implicit none + + ! 重新导出,确保兼容 + public :: wp, ip + +end module precision_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/boundary/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03g/src/boundary/CMakeLists.txt new file mode 100644 index 00000000..a9909f1e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/boundary/CMakeLists.txt @@ -0,0 +1,23 @@ +# src/boundary/CMakeLists.txt +message(STATUS "配置边界条件模块...") + +add_library(boundary STATIC + boundary_base.f90 # 基类 + periodic.f90 # 周期性边界 + dirichlet.f90 # Dirichlet边界 + neumann.f90 # Neumann边界 + factory.f90 # 工厂 +) + +target_link_libraries(boundary + PRIVATE + base + infrastructure + core # 需要注册系统 +) + +set_target_properties(boundary PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "边界条件模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/boundary/boundary_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/boundary/boundary_base.f90 new file mode 100644 index 00000000..2f7b1116 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/boundary/boundary_base.f90 @@ -0,0 +1,35 @@ +! src/boundary/boundary_base.f90 (修正版) +module boundary_base_module + use base_modules, only: wp, ip + implicit none + private + + type, abstract, public :: boundary_condition + character(len=:), allocatable :: name + contains + procedure(apply_interface), deferred :: apply + procedure :: get_name => bc_get_name + end type + + abstract interface + subroutine apply_interface(this, u, nghosts, ist, ied) + import :: boundary_condition, wp, ip + class(boundary_condition), intent(in) :: this + real(wp), intent(inout) :: u(:) + integer(ip), intent(in) :: nghosts, ist, ied + end subroutine apply_interface + end interface + +contains + + function bc_get_name(this) result(name) + class(boundary_condition), intent(in) :: this + character(len=:), allocatable :: name + if (allocated(this%name)) then + name = this%name + else + name = "unnamed" + end if + end function + +end module boundary_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/boundary/dirichlet.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/boundary/dirichlet.f90 new file mode 100644 index 00000000..e7647bea --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/boundary/dirichlet.f90 @@ -0,0 +1,55 @@ +! src/boundary/dirichlet.f90 +module dirichlet_boundary_module + use base_modules, only: wp, ip + use boundary_base_module, only: boundary_condition + implicit none + private + + type, extends(boundary_condition), public :: dirichlet_boundary + real(wp) :: left_value = 1.0_wp + real(wp) :: right_value = 2.0_wp + contains + procedure :: apply => dirichlet_apply + procedure :: set_values => dirichlet_set_values + end type + + interface dirichlet_boundary + module procedure create_dirichlet_boundary + end interface + +contains + + type(dirichlet_boundary) function create_dirichlet_boundary(left_val, right_val) result(this) + real(wp), optional, intent(in) :: left_val, right_val + + this%name = "dirichlet" + if (present(left_val)) this%left_value = left_val + if (present(right_val)) this%right_value = right_val + end function + + subroutine dirichlet_set_values(this, left_val, right_val) + class(dirichlet_boundary), intent(inout) :: this + real(wp), intent(in) :: left_val, right_val + this%left_value = left_val + this%right_value = right_val + end subroutine + + subroutine dirichlet_apply(this, u, nghosts, ist, ied) + class(dirichlet_boundary), intent(in) :: this + real(wp), intent(inout) :: u(:) + integer(ip), intent(in) :: nghosts, ist, ied + + integer :: i + + ! 左边界 + do i = 0, nghosts-1 + u(ist-1-i) = this%left_value + end do + + ! 右边界 + do i = 0, nghosts-1 + u(ied+i) = this%right_value + end do + end subroutine + +end module dirichlet_boundary_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/boundary/factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/boundary/factory.f90 new file mode 100644 index 00000000..012363b0 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/boundary/factory.f90 @@ -0,0 +1,41 @@ +! src/boundary/factory.f90 +module boundary_factory_module + use base_modules, only: wp, ip + use boundary_base_module, only: boundary_condition + use periodic_boundary_module, only: periodic_boundary + implicit none + private + + public :: create_boundary_condition, initialize_boundary_factory + +contains + + subroutine initialize_boundary_factory() + print *, "[BOUNDARY FACTORY] Boundary conditions placeholder" + end subroutine + + subroutine create_boundary_condition(bc_type, bc_instance, left_val, right_val) + character(len=*), intent(in) :: bc_type + class(boundary_condition), allocatable, intent(out) :: bc_instance + real(wp), optional, intent(in) :: left_val, right_val + + ! 暂时只实现周期性边界 + allocate(periodic_boundary :: bc_instance) + + select case (trim(bc_type)) + case ("periodic") + select type(bc => bc_instance) + type is (periodic_boundary) + bc%name = "periodic" + end select + + case default + print *, "[WARNING] Using periodic as default boundary" + select type(bc => bc_instance) + type is (periodic_boundary) + bc%name = "periodic" + end select + end select + end subroutine + +end module boundary_factory_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/boundary/neumann.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/boundary/neumann.f90 new file mode 100644 index 00000000..908869d9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/boundary/neumann.f90 @@ -0,0 +1,41 @@ +! src/boundary/neumann.f90 +module neumann_boundary_module + use base_modules, only: wp, ip + use boundary_base_module, only: boundary_condition + implicit none + private + + type, extends(boundary_condition), public :: neumann_boundary + contains + procedure :: apply => neumann_apply + end type + + interface neumann_boundary + module procedure create_neumann_boundary + end interface + +contains + + type(neumann_boundary) function create_neumann_boundary() result(this) + this%name = "neumann" + end function + + subroutine neumann_apply(this, u, nghosts, ist, ied) + class(neumann_boundary), intent(in) :: this + real(wp), intent(inout) :: u(:) + integer(ip), intent(in) :: nghosts, ist, ied + + integer :: i + + ! 左边界零梯度:u[ist-1-i] = u[ist+i] + do i = 0, nghosts-1 + u(ist-1-i) = u(ist+i) + end do + + ! 右边界零梯度:u[ied+i] = u[ied-1-i] + do i = 0, nghosts-1 + u(ied+i) = u(ied-1-i) + end do + end subroutine + +end module neumann_boundary_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/boundary/periodic.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/boundary/periodic.f90 new file mode 100644 index 00000000..1fad1f03 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/boundary/periodic.f90 @@ -0,0 +1,44 @@ +! src/boundary/periodic.f90 (修正版) +module periodic_boundary_module + use base_modules, only: wp, ip + use boundary_base_module, only: boundary_condition + implicit none + private + + type, extends(boundary_condition), public :: periodic_boundary + contains + procedure :: apply => periodic_apply + procedure :: get_name => bc_get_name + end type + +contains + + subroutine periodic_apply(this, u, nghosts, ist, ied) + class(periodic_boundary), intent(in) :: this + real(wp), intent(inout) :: u(:) + integer(ip), intent(in) :: nghosts, ist, ied + + integer :: i + + ! 左ghost层:u[ist-1-i] = u[ied-1-i] + do i = 0, nghosts-1 + u(ist-1-i) = u(ied-1-i) + end do + + ! 右ghost层:u[ied+i] = u[ist+i] + do i = 0, nghosts-1 + u(ied+i) = u(ist+i) + end do + end subroutine + + function bc_get_name(this) result(name) + class(periodic_boundary), intent(in) :: this + character(len=:), allocatable :: name + if (allocated(this%name)) then + name = this%name + else + name = "periodic" + end if + end function + +end module periodic_boundary_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/core/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03g/src/core/CMakeLists.txt new file mode 100644 index 00000000..d8b8df06 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/core/CMakeLists.txt @@ -0,0 +1,14 @@ +# src/core/CMakeLists.txt +message(STATUS "Configuring core module...") + +add_library(core STATIC + registry.f90 +) + +target_link_libraries(core PRIVATE base) + +set_target_properties(core PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Core module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/core/factory_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/core/factory_base.f90 new file mode 100644 index 00000000..302418a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/core/factory_base.f90 @@ -0,0 +1,57 @@ +! src/core/factory_base.f90 +module factory_base_module + use base_modules, only: wp, ip + use registry_module, only: create_component, has_component + + implicit none + private + public :: wp, ip, factory_base, factory_create + + ! 工厂基类 + type :: factory_base + character(len=max_name_length) :: category = "" + contains + procedure :: create => factory_base_create + procedure :: get_available => factory_base_get_available + end type factory_base + + ! 便捷函数类型 + abstract interface + function factory_function_interface(category, name) result(instance) + import :: wp + character(len=*), intent(in) :: category, name + class(*), allocatable :: instance + end function factory_function_interface + end interface + +contains + + ! 创建工厂实例 + function factory_create(category) result(factory) + character(len=*), intent(in) :: category + type(factory_base) :: factory + factory%category = trim(category) + end function factory_create + + ! 工厂创建方法 + function factory_base_create(this, name) result(instance) + class(factory_base), intent(in) :: this + character(len=*), intent(in) :: name + class(*), allocatable :: instance + + instance = create_component(this%category, name) + end function factory_base_create + + ! 获取可用组件列表(简化版) + subroutine factory_base_get_available(this, names, count) + class(factory_base), intent(in) :: this + character(len=*), allocatable, intent(out) :: names(:) + integer(ip), intent(out) :: count + + ! 这里需要实现从注册表获取列表的逻辑 + ! 暂时返回空列表 + count = 0 + allocate(character(len=max_name_length) :: names(0)) + end subroutine factory_base_get_available + +end module factory_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/core/factory_integrated.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/core/factory_integrated.f90 new file mode 100644 index 00000000..c58864c9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/core/factory_integrated.f90 @@ -0,0 +1,41 @@ +! src/core/factory_integrated.f90 +module factory_integrated + use base_modules, only: wp, ip + use registry_module, only: register_component_simple + implicit none + private + + public :: register_all_components + +contains + + subroutine register_all_components() + ! 方程 + call register_component_simple("equation", "linear_advection") + + ! 问题 + call register_component_simple("problem", "linear_advection") + + ! 重构器 + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + + ! 通量 + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist_osher") + + ! 边界条件 + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + + ! 初始条件 + call register_component_simple("initial_condition", "step") + call register_component_simple("initial_condition", "sin") + call register_component_simple("initial_condition", "gaussian") + + print *, "[FACTORY] All components registered" + end subroutine + +end module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/core/factory_interfaces.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/core/factory_interfaces.f90 new file mode 100644 index 00000000..a960167c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/core/factory_interfaces.f90 @@ -0,0 +1,17 @@ +! src/core/factory_interfaces.f90 +module factory_interfaces + use, intrinsic :: iso_fortran_env, only: wp => real64 + implicit none + + private + public :: wp, factory_procedure + + ! Factory procedure interface + abstract interface + subroutine factory_procedure(instance) + import :: wp + class(*), allocatable, intent(out) :: instance + end subroutine factory_procedure + end interface + +end module factory_interfaces \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/core/physics_solver_integrated.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/core/physics_solver_integrated.f90 new file mode 100644 index 00000000..56840e67 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/core/physics_solver_integrated.f90 @@ -0,0 +1,127 @@ +! src/solver/physics_solver_integrated.f90 +module physics_solver_integrated + use base_modules, only: wp, ip + use config_module, only: cfd_config + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create + use registry_module, only: create_component + + implicit none + private + + type, public :: physics_solver_integrated + ! 核心组件 + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + + ! 物理组件 + class(*), allocatable :: equation + class(*), allocatable :: problem + + ! 数值组件 + class(*), allocatable :: reconstructor + class(*), allocatable :: flux_calculator + class(*), allocatable :: boundary_condition + class(*), allocatable :: residual_calculator + + contains + procedure :: initialize => solver_initialize + procedure :: run => solver_run + procedure :: cleanup => solver_cleanup + procedure, private :: create_components + procedure, private :: apply_boundary + end type + +contains + + subroutine solver_initialize(this) + class(physics_solver_integrated), intent(inout) :: this + + ! 创建域和解 + this%domain = domain_create(this%config, this%mesh) + this%solution = solution_create(this%domain) + + ! 创建所有组件 + call this%create_components() + + ! 应用初始条件 + call this%apply_initial_condition() + + print *, "[SOLVER] Initialized with physics integration" + end subroutine + + subroutine create_components(this) + class(physics_solver_integrated), intent(inout) :: this + + ! 创建方程 + call create_component("equation", "linear_advection", this%config, this%equation) + + ! 创建问题 + call create_component("problem", "linear_advection", this%config, this%problem) + + ! 创建数值组件 + call create_component("reconstructor", this%config%recon_scheme, & + this%config, this%reconstructor) + call create_component("flux", this%config%flux_type, & + this%config, this%flux_calculator) + call create_component("boundary", this%config%boundary_type, & + this%cfd_context(), this%boundary_condition) + end subroutine + + subroutine apply_initial_condition(this) + class(physics_solver_integrated), intent(inout) :: this + + ! 通过问题创建初始条件 + select type(prob => this%problem) + type is (linear_advection_problem) + class(*), allocatable :: ic + call prob%create_ic(this%config, ic) + + ! 应用初始条件到解 + select type(ic_inst => ic) + type is (step_function_ic) + call ic_inst%apply(this%solution) + end select + end select + end subroutine + + function cfd_context(this) result(ctx) + class(physics_solver_integrated), intent(in) :: this + type(cfd_context_type) :: ctx + + ! 创建包含求解器所有组件的上下文 + ctx%config => this%config + ctx%domain => this%domain + ctx%solution => this%solution + ctx%equation => this%equation + end function + + subroutine solver_run(this, final_time) + class(physics_solver_integrated), intent(inout) :: this + real(wp), intent(in) :: final_time + + real(wp) :: t, dt, dt_original + integer :: step + + dt_original = this%config%dt + t = 0.0_wp + step = 0 + + do while (t < final_time - 1e-12_wp) + dt = min(this%config%dt, final_time - t) + + ! 时间步进(需要实现) + ! call this%time_step(dt) + + t = t + dt + step = step + 1 + end do + + this%config%dt = dt_original + print *, "[SOLVER] Completed at t = ", t, ", steps = ", step + end subroutine + +end module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/core/registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/core/registry.f90 new file mode 100644 index 00000000..d155aa19 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/core/registry.f90 @@ -0,0 +1,257 @@ +! src/core/registry.f90 (更新版) +module registry_module + use base_modules, only: wp, ip, max_name_len, component_info + + implicit none + private + + ! 明确公开所有需要的接口 + public :: wp, ip ! 类型参数 + public :: component_info ! 类型 + public :: registry_init, registry_cleanup ! 初始化/清理 + public :: register_component_simple ! 注册组件 + public :: has_component_simple ! 检查组件 + public :: list_components ! 列出组件 + public :: registry_is_initialized ! 检查初始化状态 + public :: registry_get_size ! 获取大小 + public :: initialize_default_components ! 新增:初始化默认组件 + + ! 全局注册表 + type :: component_registry + type(component_info), allocatable :: components(:) + integer(ip) :: count = 0 + integer(ip) :: capacity = 100 + logical :: initialized = .false. + logical :: verbose = .true. + logical :: default_components_added = .false. ! 新增:标记是否已添加默认组件 + end type component_registry + + type(component_registry) :: registry + +contains + + ! ==================== 公共API ==================== + + subroutine registry_init(verbose) + logical, optional, intent(in) :: verbose + + if (registry%initialized) then + if (registry%verbose) then + print *, "[REGISTRY] Already initialized" + end if + return + end if + + if (present(verbose)) then + registry%verbose = verbose + end if + + allocate(registry%components(registry%capacity)) + registry%initialized = .true. + + if (registry%verbose) then + print *, "[REGISTRY] Initialized with capacity:", registry%capacity + end if + end subroutine registry_init + + subroutine registry_cleanup() + if (allocated(registry%components)) then + deallocate(registry%components) + end if + registry%initialized = .false. + registry%count = 0 + registry%default_components_added = .false. ! 重置标记 + + if (registry%verbose) then + print *, "[REGISTRY] Cleaned up" + end if + end subroutine registry_cleanup + + ! 新增:初始化默认组件 + subroutine initialize_default_components() + if (.not. registry%initialized) then + call registry_init() + end if + + if (registry%default_components_added) then + if (registry%verbose) then + print *, "[REGISTRY] Default components already added" + end if + return + end if + + ! 注册重构器 + call register_component_simple("reconstructor", "eno", order=3) + call register_component_simple("reconstructor", "weno3", order=3) + call register_component_simple("reconstructor", "weno5", order=5) + + ! 注册通量计算器 + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + + ! 注册边界条件 + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + + ! 注册时间积分器 + call register_component_simple("integrator", "rk1", order=1) + call register_component_simple("integrator", "rk2", order=2) + call register_component_simple("integrator", "rk3", order=3) + + ! 注册方程 + call register_component_simple("equation", "linear_advection") + + ! 注册问题 + call register_component_simple("problem", "linear_advection") + + ! 注册初始条件 + call register_component_simple("initial_condition", "step") + call register_component_simple("initial_condition", "sin") + call register_component_simple("initial_condition", "gaussian") + + registry%default_components_added = .true. + + if (registry%verbose) then + print *, "[REGISTRY] Default components registered" + print *, "[REGISTRY] Total components:", registry%count + end if + end subroutine initialize_default_components + + subroutine register_component_simple(category, name, order) + character(len=*), intent(in) :: category, name + integer(ip), optional, intent(in) :: order + + integer(ip) :: i + type(component_info) :: info + + if (.not. registry%initialized) then + call registry_init() + end if + + ! 检查是否已存在 + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + if (registry%verbose) then + print *, "[WARN] Overwriting component: ", trim(category), ".", trim(name) + end if + + ! 更新 + if (present(order)) then + registry%components(i)%order = order + else + registry%components(i)%order = 0 + end if + return + end if + end do + + ! 扩展数组 + if (registry%count >= registry%capacity) then + call expand_registry() + end if + + ! 添加新组件 + registry%count = registry%count + 1 + + info%category = trim(category) + info%name = trim(name) + info%order = 0 + if (present(order)) then + info%order = order + end if + + registry%components(registry%count) = info + + if (registry%verbose) then + print *, "[OK] Registered simple: ", trim(category), ".", trim(name) + end if + end subroutine register_component_simple + + logical function has_component_simple(category, name) + character(len=*), intent(in) :: category, name + + integer(ip) :: i + + has_component_simple = .false. + + if (.not. registry%initialized) return + + do i = 1, registry%count + if (trim(registry%components(i)%category) == trim(category) .and. & + trim(registry%components(i)%name) == trim(name)) then + has_component_simple = .true. + return + end if + end do + end function has_component_simple + + subroutine list_components(category) + character(len=*), optional, intent(in) :: category + + integer(ip) :: i, count + + if (.not. registry%initialized) then + print *, "[INFO] Registry not initialized" + return + end if + + if (registry%count == 0) then + print *, "[INFO] No components registered" + return + end if + + count = 0 + print *, "=== Registry Contents ===" + do i = 1, registry%count + if (.not. present(category) .or. & + trim(registry%components(i)%category) == trim(category)) then + call print_component_info(registry%components(i)) + count = count + 1 + end if + end do + + print *, "Total:", count, "components" + print *, "==========================" + end subroutine list_components + + ! ==================== 新增函数 ==================== + + logical function registry_is_initialized() + ! 检查注册表是否已初始化 + registry_is_initialized = registry%initialized + end function registry_is_initialized + + integer(ip) function registry_get_size() + ! 获取注册表中的组件数量 + registry_get_size = registry%count + end function registry_get_size + + ! ==================== 内部辅助函数 ==================== + + subroutine expand_registry() + type(component_info), allocatable :: temp(:) + + registry%capacity = registry%capacity * 2 + allocate(temp(registry%capacity)) + temp(1:registry%count) = registry%components(1:registry%count) + call move_alloc(temp, registry%components) + + if (registry%verbose) then + print *, "[INFO] Registry expanded to capacity:", registry%capacity + end if + end subroutine expand_registry + + subroutine print_component_info(info) + type(component_info), intent(in) :: info + + if (info%order > 0) then + print *, " [", trim(info%category), ".", trim(info%name), & + " (order:", info%order, ")]" + else + print *, " [", trim(info%category), ".", trim(info%name), "]" + end if + end subroutine print_component_info + +end module registry_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/core/registry_initializer.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/core/registry_initializer.f90 new file mode 100644 index 00000000..44023d1d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/core/registry_initializer.f90 @@ -0,0 +1,39 @@ +! src/core/registry_initializer.f90 (新增文件) +module registry_initializer_module + use registry_module, only: register_component_simple + implicit none + private + public :: initialize_default_registry + +contains + + subroutine initialize_default_registry() + ! 注册重构器 + call register_component_simple("reconstructor", "eno", order=3) + call register_component_simple("reconstructor", "weno3", order=3) + call register_component_simple("reconstructor", "weno5", order=5) + + ! 注册通量计算器 + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + + ! 注册边界条件 + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + + ! 注册时间积分器 + call register_component_simple("integrator", "rk1", order=1) + call register_component_simple("integrator", "rk2", order=2) + call register_component_simple("integrator", "rk3", order=3) + + ! 注册方程 + call register_component_simple("equation", "linear_advection") + + ! 注册问题 + call register_component_simple("problem", "linear_advection") + + print *, "[REGISTRY] Default components registered" + end subroutine initialize_default_registry + +end module registry_initializer_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/infrastructure/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03g/src/infrastructure/CMakeLists.txt new file mode 100644 index 00000000..70cbbd2f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/infrastructure/CMakeLists.txt @@ -0,0 +1,17 @@ +# src/infrastructure/CMakeLists.txt +message(STATUS "Configuring infrastructure module...") + +add_library(infrastructure STATIC + config.f90 + mesh.f90 + domain.f90 # 新增 + solution.f90 # 新增 +) + +target_link_libraries(infrastructure PRIVATE base) + +set_target_properties(infrastructure PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "Infrastructure module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/infrastructure/config.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/infrastructure/config.f90 new file mode 100644 index 00000000..7586a1a5 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/infrastructure/config.f90 @@ -0,0 +1,144 @@ +! src/infrastructure/config.f90 (修复版) +module config_module + use base_modules, only: wp, ip, max_name_len, cfd_config_base + + implicit none + public :: wp, ip, cfd_config, config_print, config_with_reconstruction + + ! 扩展配置类型 - 添加物理相关字段 + type, extends(cfd_config_base) :: cfd_config + ! 物理参数 + real(wp) :: left_boundary_value = 1.0_wp + real(wp) :: right_boundary_value = 2.0_wp + real(wp) :: domain_length = 2.0_wp + + ! 新增:物理模块相关配置 + real(wp) :: pulse_center = 0.5_wp ! 高斯脉冲中心 + real(wp) :: pulse_width = 0.1_wp ! 高斯脉冲宽度 + logical :: enable_physics = .true. ! 是否启用物理模块 + contains + ! 新增:物理相关配置方法 + procedure :: set_physics_parameters + procedure :: get_physics_info + end type cfd_config + +contains + + subroutine config_print(cfg) + type(cfd_config), intent(in) :: cfg + + print *, "=== CFD Configuration ===" + print *, "Initial condition: ", trim(cfg%ic_type) + print *, "Reconstruction: ", trim(cfg%recon_scheme), " (order:", cfg%spatial_order, ")" + print *, "Flux type: ", trim(cfg%flux_type) + print *, "Time integration: RK", cfg%rk_order + print *, "Wave speed: ", cfg%wave_speed + print *, "Final time: ", cfg%final_time + print *, "Time step: ", cfg%dt + print *, "Boundary: ", trim(cfg%boundary_type) + + ! 新增:物理配置信息 + print *, "--- Physics Configuration ---" + print *, "Equation type: ", trim(cfg%equation_type) + print *, "Problem type: ", trim(cfg%problem_type) + print *, "Domain length: ", cfg%domain_length + print *, "Physics enabled: ", cfg%enable_physics + + if (cfg%ic_type == "gaussian") then + print *, "Pulse center: ", cfg%pulse_center + print *, "Pulse width: ", cfg%pulse_width + end if + + print *, "===============================" + end subroutine config_print + + subroutine config_with_reconstruction(cfg, scheme, order) + type(cfd_config), intent(inout) :: cfg + character(len=*), intent(in) :: scheme + integer, optional, intent(in) :: order + + integer :: i + + ! 转换为小写 + cfg%recon_scheme = scheme + do i = 1, len_trim(cfg%recon_scheme) + if (cfg%recon_scheme(i:i) >= 'A' .and. cfg%recon_scheme(i:i) <= 'Z') then + cfg%recon_scheme(i:i) = char(ichar(cfg%recon_scheme(i:i)) + 32) + end if + end do + + ! 设置阶数 + if (present(order)) then + cfg%spatial_order = order + else + if (index(cfg%recon_scheme, 'weno') > 0) then + cfg%spatial_order = 5 + else if (trim(cfg%recon_scheme) == 'eno') then + cfg%spatial_order = 3 + end if + end if + + if (cfg%verbose) then + print *, "[CONFIG] Reconstruction: ", trim(cfg%recon_scheme), & + " Order: ", cfg%spatial_order + end if + end subroutine config_with_reconstruction + + ! ========== 新增:物理参数设置方法 ========== + + subroutine set_physics_parameters(this, equation_type, problem_type, & + domain_length, enable_physics) + class(cfd_config), intent(inout) :: this + character(len=*), intent(in), optional :: equation_type, problem_type + real(wp), intent(in), optional :: domain_length + logical, intent(in), optional :: enable_physics + + if (present(equation_type)) then + this%equation_type = trim(equation_type) + if (this%verbose) then + print *, "[CONFIG] Set equation type: ", trim(this%equation_type) + end if + end if + + if (present(problem_type)) then + this%problem_type = trim(problem_type) + if (this%verbose) then + print *, "[CONFIG] Set problem type: ", trim(this%problem_type) + end if + end if + + if (present(domain_length)) then + this%domain_length = domain_length + if (this%verbose) then + print *, "[CONFIG] Set domain length: ", this%domain_length + end if + end if + + if (present(enable_physics)) then + this%enable_physics = enable_physics + if (this%verbose) then + print *, "[CONFIG] Physics module enabled: ", this%enable_physics + end if + end if + end subroutine set_physics_parameters + + subroutine get_physics_info(this) + class(cfd_config), intent(in) :: this + + print *, "=== Physics Configuration Info ===" + print *, "Equation type: ", trim(this%equation_type) + print *, "Problem type: ", trim(this%problem_type) + print *, "Domain length: ", this%domain_length + print *, "Wave speed: ", this%wave_speed + print *, "Physics enabled: ", this%enable_physics + + if (this%ic_type == "gaussian") then + print *, "Pulse parameters:" + print *, " Center: ", this%pulse_center + print *, " Width: ", this%pulse_width + end if + + print *, "==================================" + end subroutine get_physics_info + +end module config_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/infrastructure/domain.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/infrastructure/domain.f90 new file mode 100644 index 00000000..641a9068 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/infrastructure/domain.f90 @@ -0,0 +1,102 @@ +! src/infrastructure/domain.f90(修正版) +module domain_module + use base_modules, only: wp, ip, max_name_len + use config_module, only: cfd_config + use mesh_module, only: mesh_type + + implicit none + private + public :: wp, ip, domain_type, domain_create, is_physical_cell + + type :: domain_type + type(cfd_config), pointer :: config => null() + type(mesh_type), pointer :: mesh => null() + integer(ip) :: nghosts = 0 + integer(ip) :: ist = 1 ! 物理区域起始索引(1-based) + integer(ip) :: ied = 1 ! 物理区域结束索引(exclusive) + integer(ip) :: ntcells = 0 ! 总单元数(含ghost) + contains + procedure :: print_info => domain_print_info + procedure :: get_physical_indices => domain_get_physical_indices + end type domain_type + +contains + + function domain_create(config, mesh) result(domain) + type(cfd_config), target, intent(in) :: config + type(mesh_type), target, intent(in) :: mesh + type(domain_type) :: domain + + domain%config => config + domain%mesh => mesh + + ! 计算ghost层数(参考Julia的_calc_nghosts) + domain%nghosts = calc_nghosts(config) + domain%ist = domain%nghosts + 1 + domain%ied = domain%ist + mesh%ncells + domain%ntcells = mesh%ncells + 2 * domain%nghosts + + if (config%verbose) then + print *, "[DOMAIN] Created:" + print *, " Ghost layers: ", domain%nghosts + print *, " Physical cells: ", domain%ist, " to ", domain%ied - 1 + print *, " Total cells: ", domain%ntcells + end if + end function domain_create + + function calc_nghosts(config) result(nghosts) + type(cfd_config), intent(in) :: config + integer(ip) :: nghosts + + character(len=max_name_len) :: scheme + + scheme = config%recon_scheme + + if (scheme == "eno") then + nghosts = config%spatial_order + else if (index(scheme, "weno") > 0) then + nghosts = config%spatial_order / 2 + 1 + else + print *, "[WARNING] Unknown scheme, using default nghosts=2" + nghosts = 2 + end if + + if (nghosts <= 0) then + print *, "[ERROR] Invalid nghosts: ", nghosts + nghosts = 2 + end if + end function calc_nghosts + + logical function is_physical_cell(this, idx) + class(domain_type), intent(in) :: this + integer(ip), intent(in) :: idx + is_physical_cell = (idx >= this%ist .and. idx < this%ied) + end function is_physical_cell + + function domain_get_physical_indices(this) result(indices) + class(domain_type), intent(in) :: this + integer(ip), allocatable :: indices(:) + integer(ip) :: i, count + + count = this%ied - this%ist + allocate(indices(count)) + + do i = 1, count + indices(i) = this%ist + i - 1 + end do + end function domain_get_physical_indices + + subroutine domain_print_info(this) + class(domain_type), intent(in) :: this + + print *, "=== Domain Information ===" + print *, "Configuration: ", trim(this%config%recon_scheme), & + " order ", this%config%spatial_order + print *, "Ghost layers: ", this%nghosts + print *, "Physical cells: ", this%ist, " to ", this%ied - 1 + print *, "Total cells: ", this%ntcells + print *, "Mesh cells: ", this%mesh%ncells + print *, "==========================" + end subroutine domain_print_info + +end module domain_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/infrastructure/mesh.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/infrastructure/mesh.f90 new file mode 100644 index 00000000..f810f3a1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/infrastructure/mesh.f90 @@ -0,0 +1,73 @@ +! src/infrastructure/mesh.f90 +module mesh_module + use base_modules, only: wp, ip + + implicit none + public :: wp, ip, mesh_type, mesh_init, mesh_print_info + + ! 网格类型 + type :: mesh_type + real(wp) :: xmin = 0.0_wp + real(wp) :: xmax = 2.0_wp + integer(ip) :: ncells = 40 + integer(ip) :: nnodes + integer(ip) :: nx + real(wp), allocatable :: x(:) ! 节点坐标 + real(wp), allocatable :: xcc(:) ! 单元中心坐标 + real(wp) :: L, dx + contains + procedure :: init => mesh_init + procedure :: print_info => mesh_print_info + end type mesh_type + +contains + + subroutine mesh_init(this, xmin, xmax, ncells) + class(mesh_type), intent(inout) :: this + real(wp), optional, intent(in) :: xmin, xmax + integer(ip), optional, intent(in) :: ncells + + integer(ip) :: i + + ! 设置参数 + if (present(xmin)) this%xmin = xmin + if (present(xmax)) this%xmax = xmax + if (present(ncells)) this%ncells = ncells + + ! 计算 + this%nnodes = this%ncells + 1 + this%nx = this%ncells + this%L = this%xmax - this%xmin + this%dx = this%L / real(this%ncells, wp) + + ! 分配内存 + if (allocated(this%x)) deallocate(this%x) + if (allocated(this%xcc)) deallocate(this%xcc) + + allocate(this%x(this%nnodes)) + allocate(this%xcc(this%ncells)) + + ! 生成节点坐标 + do i = 1, this%nnodes + this%x(i) = this%xmin + (i - 1) * this%dx + end do + + ! 生成单元中心坐标 + do i = 1, this%ncells + this%xcc(i) = 0.5_wp * (this%x(i) + this%x(i + 1)) + end do + end subroutine mesh_init + + subroutine mesh_print_info(this) + class(mesh_type), intent(in) :: this + + print *, "=== Mesh Information ===" + print *, "Domain: [", this%xmin, ", ", this%xmax, "]" + print *, "Cells: ", this%ncells + print *, "Nodes: ", this%nnodes + print *, "dx: ", this%dx + print *, "L: ", this%L + print *, "========================" + end subroutine mesh_print_info + +end module mesh_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/infrastructure/solution.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/infrastructure/solution.f90 new file mode 100644 index 00000000..35e79671 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/infrastructure/solution.f90 @@ -0,0 +1,131 @@ +! src/infrastructure/solution.f90(修正版) +module solution_module + use base_modules, only: wp, ip + use domain_module, only: domain_type + + implicit none + private + public :: wp, ip, solution_type, solution_create, solution_reset + + type :: solution_type + type(domain_type), pointer :: domain => null() + real(wp), allocatable :: u(:) ! 当前解(含ghost) + real(wp), allocatable :: un(:) ! 旧解 + real(wp), allocatable :: q_face_left(:) ! 左界面值 + real(wp), allocatable :: q_face_right(:)! 右界面值 + real(wp), allocatable :: flux(:) ! 通量 + real(wp), allocatable :: res(:) ! 残差 + contains + procedure :: initialize => solution_initialize + procedure :: update_old_field => solution_update_old_field + procedure :: print_info => solution_print_info + procedure :: reset => solution_reset_instance + end type solution_type + +contains + + function solution_create(domain) result(solution) + type(domain_type), target, intent(in) :: domain + type(solution_type) :: solution + + integer(ip) :: ncells, nnodes, ntcells + + solution%domain => domain + + ncells = domain%mesh%ncells + nnodes = domain%mesh%nnodes + ntcells = domain%ntcells + + ! 分配数组(与Julia solution.jl一致) + allocate(solution%u(ntcells), source=0.0_wp) + allocate(solution%un(ntcells), source=0.0_wp) + allocate(solution%q_face_left(nnodes), source=0.0_wp) + allocate(solution%q_face_right(nnodes), source=0.0_wp) + allocate(solution%flux(nnodes), source=0.0_wp) + allocate(solution%res(ncells), source=0.0_wp) + + if (domain%config%verbose) then + print *, "[SOLUTION] Created:" + print *, " u size: ", size(solution%u), " (with ghosts)" + print *, " flux size: ", size(solution%flux) + print *, " res size: ", size(solution%res) + end if + end function solution_create + + subroutine solution_initialize(this, initial_values) + class(solution_type), intent(inout) :: this + real(wp), intent(in), optional :: initial_values(:) + + integer(ip) :: i, idx + type(domain_type), pointer :: domain + + domain => this%domain + + if (present(initial_values)) then + ! 应用初始值到物理区域 + do i = domain%ist, domain%ied - 1 + idx = i - domain%ist + 1 + if (idx <= size(initial_values)) then + this%u(i) = initial_values(idx) + end if + end do + else + ! 默认为0 + this%u = 0.0_wp + end if + + ! 同步旧场(与Julia的update_old_field一致) + call this%update_old_field() + + if (domain%config%verbose) then + print *, "[SOLUTION] Initialized" + print *, " u range: ", minval(this%u), " to ", maxval(this%u) + end if + end subroutine solution_initialize + + subroutine solution_update_old_field(this) + class(solution_type), intent(inout) :: this + this%un = this%u ! 与Julia的 un .= u 一致 + end subroutine solution_update_old_field + + subroutine solution_reset_instance(this) + class(solution_type), intent(inout) :: this + call solution_reset(this) + end subroutine solution_reset_instance + + subroutine solution_reset(solution) + type(solution_type), intent(inout) :: solution + + if (allocated(solution%u)) solution%u = 0.0_wp + if (allocated(solution%un)) solution%un = 0.0_wp + if (allocated(solution%q_face_left)) solution%q_face_left = 0.0_wp + if (allocated(solution%q_face_right)) solution%q_face_right = 0.0_wp + if (allocated(solution%flux)) solution%flux = 0.0_wp + if (allocated(solution%res)) solution%res = 0.0_wp + + if (associated(solution%domain) .and. solution%domain%config%verbose) then + print *, "[SOLUTION] Reset" + end if + end subroutine solution_reset + + subroutine solution_print_info(this) + class(solution_type), intent(in) :: this + + print *, "=== Solution Information ===" + print *, "Arrays:" + print *, " u: ", size(this%u), " elements" + print *, " un: ", size(this%un), " elements" + print *, " q_face_left: ", size(this%q_face_left), " elements" + print *, " q_face_right: ", size(this%q_face_right), " elements" + print *, " flux: ", size(this%flux), " elements" + print *, " res: ", size(this%res), " elements" + + if (allocated(this%u)) then + print *, "Values:" + print *, " u min/max: ", minval(this%u), maxval(this%u) + print *, " un min/max: ", minval(this%un), maxval(this%un) + end if + print *, "============================" + end subroutine solution_print_info + +end module solution_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/initial_condition/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03g/src/initial_condition/CMakeLists.txt new file mode 100644 index 00000000..01fbfa47 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/initial_condition/CMakeLists.txt @@ -0,0 +1,23 @@ +# src/initial_condition/CMakeLists.txt +message(STATUS "配置初始条件模块...") + +add_library(initial_condition STATIC + ic_base.f90 # 基类 + step.f90 # 阶跃函数 + sine.f90 # 正弦波 + gaussian.f90 # 高斯脉冲 + factory.f90 # 工厂 +) + +target_link_libraries(initial_condition + PRIVATE + base + infrastructure + core # 需要注册系统 +) + +set_target_properties(initial_condition PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "初始条件模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/initial_condition/factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/initial_condition/factory.f90 new file mode 100644 index 00000000..7fbdd522 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/initial_condition/factory.f90 @@ -0,0 +1,31 @@ +! src/initial_condition/factory.f90 +module ic_factory_module + use base_modules, only: wp, ip + use ic_base_module, only: initial_condition + use step_ic_module, only: step_function_ic + implicit none + private + + public :: create_initial_condition, initialize_ic_factory + +contains + + subroutine initialize_ic_factory() + print *, "[IC FACTORY] Initial conditions placeholder" + end subroutine + + subroutine create_initial_condition(ic_type, ic_instance) + character(len=*), intent(in) :: ic_type + class(initial_condition), allocatable, intent(out) :: ic_instance + + ! 暂时只实现步函数 + allocate(step_function_ic :: ic_instance) + + select type(ic => ic_instance) + type is (step_function_ic) + ! 使用默认构造函数 + ic%name = "step" + end select + end subroutine + +end module ic_factory_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/initial_condition/gaussian.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/initial_condition/gaussian.f90 new file mode 100644 index 00000000..ed2b625b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/initial_condition/gaussian.f90 @@ -0,0 +1,62 @@ +! src/initial_condition/gaussian.f90 +module gaussian_ic_module + use base_modules, only: wp, ip + use ic_base_module, only: initial_condition + use solution_module, only: solution_type + use domain_module, only: domain_type + implicit none + private + + type, extends(initial_condition), public :: gaussian_pulse_ic + contains + procedure :: evaluate_at => gaussian_evaluate_at + procedure :: apply => gaussian_apply + end type + + interface gaussian_pulse_ic + module procedure create_gaussian_pulse_ic + end interface + +contains + + type(gaussian_pulse_ic) function create_gaussian_pulse_ic() result(this) + this%name = "gaussian" + end function + + function gaussian_evaluate_at(this, x) result(u) + class(gaussian_pulse_ic), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp) :: u(size(x)) + + integer :: i + real(wp) :: center, width + + center = 0.5_wp ! 脉冲中心 + width = 0.1_wp ! 脉冲宽度 + + do i = 1, size(x) + u(i) = exp(-((x(i) - center) / width)**2) + end do + end function + + subroutine gaussian_apply(this, solution) + class(gaussian_pulse_ic), intent(in) :: this + type(solution_type), intent(inout) :: solution + + integer :: i, idx + real(wp), allocatable :: u0(:) + type(domain_type), pointer :: domain + + domain => solution%domain + + ! 评估初始条件 + u0 = this%evaluate_at(domain%mesh%xcc) + + ! 应用到物理区域 + do i = domain%ist, domain%ied - 1 + idx = i - domain%ist + 1 + solution%u(i) = u0(idx) + end do + end subroutine + +end module gaussian_ic_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/initial_condition/ic_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/initial_condition/ic_base.f90 new file mode 100644 index 00000000..4a7845fe --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/initial_condition/ic_base.f90 @@ -0,0 +1,43 @@ +! src/initial_condition/ic_base.f90 +module ic_base_module + use base_modules, only: wp, ip + use solution_module, only: solution_type + implicit none + private + + type, abstract, public :: initial_condition + character(len=:), allocatable :: name + contains + procedure :: get_name => ic_get_name + procedure(evaluate_interface), deferred :: evaluate_at + procedure(apply_interface), deferred :: apply + end type + + abstract interface + function evaluate_interface(this, x) result(u) + import :: initial_condition, wp + class(initial_condition), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp) :: u(size(x)) + end function evaluate_interface + + subroutine apply_interface(this, solution) + import :: initial_condition, solution_type + class(initial_condition), intent(in) :: this + type(solution_type), intent(inout) :: solution + end subroutine apply_interface + end interface + +contains + + function ic_get_name(this) result(name) + class(initial_condition), intent(in) :: this + character(len=:), allocatable :: name + if (allocated(this%name)) then + name = this%name + else + name = "unnamed" + end if + end function + +end module ic_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/initial_condition/sine.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/initial_condition/sine.f90 new file mode 100644 index 00000000..f9f1028c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/initial_condition/sine.f90 @@ -0,0 +1,62 @@ +! src/initial_condition/sine.f90 +module sine_ic_module + use base_modules, only: wp, ip + use ic_base_module, only: initial_condition + use solution_module, only: solution_type + use domain_module, only: domain_type + implicit none + private + + type, extends(initial_condition), public :: sine_wave_ic + contains + procedure :: evaluate_at => sine_evaluate_at + procedure :: apply => sine_apply + end type + + interface sine_wave_ic + module procedure create_sine_wave_ic + end interface + +contains + + type(sine_wave_ic) function create_sine_wave_ic() result(this) + this%name = "sin" + end function + + function sine_evaluate_at(this, x) result(u) + class(sine_wave_ic), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp) :: u(size(x)) + + integer :: i + real(wp) :: L + + ! 假设域长度,可以根据需要调整 + L = 2.0_wp ! 默认域长度 + + do i = 1, size(x) + u(i) = sin(2.0_wp * 3.141592653589793_wp * x(i) / L) + end do + end function + + subroutine sine_apply(this, solution) + class(sine_wave_ic), intent(in) :: this + type(solution_type), intent(inout) :: solution + + integer :: i, idx + real(wp), allocatable :: u0(:) + type(domain_type), pointer :: domain + + domain => solution%domain + + ! 评估初始条件 + u0 = this%evaluate_at(domain%mesh%xcc) + + ! 应用到物理区域 + do i = domain%ist, domain%ied - 1 + idx = i - domain%ist + 1 + solution%u(i) = u0(idx) + end do + end subroutine + +end module sine_ic_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/initial_condition/step.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/initial_condition/step.f90 new file mode 100644 index 00000000..dca60265 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/initial_condition/step.f90 @@ -0,0 +1,62 @@ +! src/initial_condition/step.f90 +module step_ic_module + use base_modules, only: wp, ip + use ic_base_module, only: initial_condition + use solution_module, only: solution_type + use domain_module, only: domain_type + implicit none + private + + type, extends(initial_condition), public :: step_function_ic + contains + procedure :: evaluate_at => step_evaluate_at + procedure :: apply => step_apply + end type + + interface step_function_ic + module procedure create_step_function_ic + end interface + +contains + + type(step_function_ic) function create_step_function_ic() result(this) + this%name = "step" + end function + + function step_evaluate_at(this, x) result(u) + class(step_function_ic), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp) :: u(size(x)) + + integer :: i + + do i = 1, size(x) + if (x(i) >= 0.5_wp .and. x(i) <= 1.0_wp) then + u(i) = 2.0_wp + else + u(i) = 1.0_wp + end if + end do + end function + + subroutine step_apply(this, solution) + class(step_function_ic), intent(in) :: this + type(solution_type), intent(inout) :: solution + + integer :: i, idx + real(wp), allocatable :: u0(:) + type(domain_type), pointer :: domain + + domain => solution%domain + + ! 评估初始条件 + u0 = this%evaluate_at(domain%mesh%xcc) + + ! 应用到物理区域 + do i = domain%ist, domain%ied - 1 + idx = i - domain%ist + 1 + solution%u(i) = u0(idx) + end do + end subroutine + +end module step_ic_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/manager/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03g/src/manager/CMakeLists.txt new file mode 100644 index 00000000..00c8bf49 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/manager/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/manager/CMakeLists.txt +message(STATUS "配置管理器模块...") + +# 创建管理器库 +add_library(manager STATIC + component_manager.f90 + component_factory.f90 +) + +# 明确依赖关系:管理器依赖所有其他模块 +target_link_libraries(manager + PRIVATE + core + infrastructure + reconstructor + flux +) + +# 设置模块输出目录 +set_target_properties(manager PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "管理器模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/manager/component_factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/manager/component_factory.f90 new file mode 100644 index 00000000..bafceddb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/manager/component_factory.f90 @@ -0,0 +1,127 @@ +! src/manager/component_factory.f90 +module component_factory_module + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use weno5_reconstructor_module, only: weno5_reconstructor + use rusanov_flux_module, only: rusanov_flux + + implicit none + private + public :: wp, create_reconstructor, create_flux_calculator + + ! 定义wp以保持兼容性 + integer, parameter :: wp = real64 + + ! 错误代码 + integer, parameter :: CM_SUCCESS = 0 + integer, parameter :: CM_ERROR_UNKNOWN_SCHEME = 1 + integer, parameter :: CM_ERROR_UNKNOWN_FLUX = 2 + integer, parameter :: CM_ERROR_INVALID_ORDER = 3 + +contains + + ! ==================== 重构器创建 ==================== + + function create_reconstructor(config, status) result(recon) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(reconstructor_base), allocatable :: recon + + character(len=20) :: scheme + integer :: order, error_code + + scheme = trim(adjustl(config%recon_scheme)) + order = config%spatial_order + + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating reconstructor: ", scheme, " order=", order + end if + + ! 处理"weno"作为WENO5的别名 + if (scheme == "weno" .and. order == 5) then + scheme = "weno5" + end if + + select case(scheme) + case('eno') + allocate(eno_reconstructor :: recon) + select type(recon) + type is(eno_reconstructor) + recon%order = order + end select + + case('weno3') + allocate(weno3_reconstructor :: recon) + select type(recon) + type is(weno3_reconstructor) + recon%order = order + end select + + case('weno5') + allocate(weno5_reconstructor :: recon) + select type(recon) + type is(weno5_reconstructor) + recon%order = order + end select + + case default + error_code = CM_ERROR_UNKNOWN_SCHEME + if (config%verbose) then + print *, "[ERROR] Unknown reconstructor scheme: ", scheme + print *, " Available: eno, weno3, weno5" + end if + end select + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Reconstructor creation failed" + end if + end function create_reconstructor + + ! ==================== 通量计算器创建 ==================== + + function create_flux_calculator(config, status) result(flux) + type(cfd_config), intent(in) :: config + integer, optional, intent(out) :: status + class(flux_calculator_base), allocatable :: flux + + character(len=20) :: flux_type + integer :: error_code + + flux_type = trim(adjustl(config%flux_type)) + error_code = CM_SUCCESS + + if (config%verbose) then + print *, "[COMPONENT FACTORY] Creating flux calculator: ", flux_type + end if + + select case(flux_type) + case('rusanov') + allocate(rusanov_flux :: flux) + + case default + error_code = CM_ERROR_UNKNOWN_FLUX + if (config%verbose) then + print *, "[ERROR] Unknown flux type: ", flux_type + print *, " Available: rusanov" + end if + end select + + ! 设置状态码 + if (present(status)) then + status = error_code + else if (error_code /= CM_SUCCESS) then + error stop "Flux calculator creation failed" + end if + end function create_flux_calculator + +end module component_factory_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/manager/component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/manager/component_manager.f90 new file mode 100644 index 00000000..70d00991 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/manager/component_manager.f90 @@ -0,0 +1,48 @@ +! src/manager/component_manager.f90 +module component_manager_module + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config + + implicit none + private + public :: wp, component_manager_info, validate_config + + ! 定义wp以保持兼容性 + integer, parameter :: wp = real64 + +contains + + ! ==================== 配置验证 ==================== + + function validate_config(config) result(is_valid) + type(cfd_config), intent(in) :: config + logical :: is_valid + + ! 简单验证 + is_valid = .true. + + if (config%verbose) then + print *, "[CONFIG VALIDATION] Configuration is valid (simplified)" + end if + end function validate_config + + ! ==================== 信息显示 ==================== + + subroutine component_manager_info() + print *, "=== Component Manager ===" + print *, "Available reconstructors:" + print *, " - eno (orders: 1-7)" + print *, " - weno3 (order: 3)" + print *, " - weno5 (order: 5)" + print *, "" + print *, "Available flux calculators:" + print *, " - rusanov" + print *, "" + print *, "Features:" + print *, " - Configuration validation" + print *, " - Component creation from config" + print *, " - Error handling with status codes" + print *, "=========================" + end subroutine component_manager_info + +end module component_manager_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/CMakeLists.txt new file mode 100644 index 00000000..95996da6 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/CMakeLists.txt @@ -0,0 +1,8 @@ +# src/numerics/CMakeLists.txt +message(STATUS "配置数值方法模块...") + +add_subdirectory(reconstructor) +add_subdirectory(flux) +add_subdirectory(time_integration) # 新增 + +message(STATUS "数值方法模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/flux/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/flux/CMakeLists.txt new file mode 100644 index 00000000..3fde7746 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/flux/CMakeLists.txt @@ -0,0 +1,28 @@ +# src/numerics/flux/CMakeLists.txt +message(STATUS "Configuring flux calculator module...") + +# Start with just the base module +add_library(flux STATIC + base.f90 + rusanov.f90 +) + +#target_link_libraries(flux PRIVATE core infrastructure) + +target_include_directories(flux PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 明确设置不使用 /Qm64 选项 +if(CMAKE_Fortran_COMPILER_ID MATCHES "IntelLLVM|Intel") + set_target_properties(flux PROPERTIES + Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /Qm64" # 明确设置,避免继承问题 + ) +endif() + +install(TARGETS flux + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Flux calculator module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/flux/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/flux/base.f90 new file mode 100644 index 00000000..7080a7ae --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/flux/base.f90 @@ -0,0 +1,30 @@ +!src/numerics/flux/base.f90 +module flux_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, flux_calculator_base + + ! Base flux calculator type + type :: flux_calculator_base + character(len=20) :: name = "Base" + contains + procedure :: info => flux_info + procedure :: print_basic_info => flux_print_basic ! 添加辅助方法 + end type flux_calculator_base + +contains + + subroutine flux_info(this) + class(flux_calculator_base), intent(in) :: this + print *, "Flux calculator information:" + print *, " Name: ", trim(this%name) + end subroutine flux_info + + subroutine flux_print_basic(this) + class(flux_calculator_base), intent(in) :: this + print *, " Name: ", trim(this%name) + end subroutine flux_print_basic + +end module flux_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/flux/engquist_osher.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/flux/engquist_osher.f90 new file mode 100644 index 00000000..90f499d1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/flux/engquist_osher.f90 @@ -0,0 +1,41 @@ +! src/numerics/flux/engquist_osher.f90 +module engquist_osher_flux + use base_modules, only: wp, ip + use flux_base_module, only: flux_calculator_base + implicit none + private + + type, extends(flux_calculator_base), public :: engquist_osher_flux_t + real(wp) :: wave_speed = 1.0_wp + contains + procedure :: compute => eo_compute + end type + +contains + + subroutine eo_compute(this, qL, qR, flux, equation) + class(engquist_osher_flux_t), intent(inout) :: this + real(wp), intent(in) :: qL(:), qR(:) + real(wp), intent(out) :: flux(:) + class(*), intent(in) :: equation + + integer :: i, n + real(wp) :: cp, cm, uL, uR + + select type(eq => equation) + type is (linear_advection_eq) + cp = 0.5_wp * (eq%wave_speed + abs(eq%wave_speed)) + cm = 0.5_wp * (eq%wave_speed - abs(eq%wave_speed)) + + n = size(qL) + do i = 1, n + uL = qL(i) + uR = qR(i) + flux(i) = cp * uL + cm * uR + end do + class default + error stop "Unsupported equation type for Engquist-Osher flux" + end select + end subroutine + +end module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/flux/rusanov.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/flux/rusanov.f90 new file mode 100644 index 00000000..daa9e3bb --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/flux/rusanov.f90 @@ -0,0 +1,41 @@ +! src/numerics/flux/rusanov.f90 +module rusanov_flux_module + use, intrinsic :: iso_fortran_env, only: real64 + use flux_base_module, only: flux_calculator_base + implicit none + + private + public :: real64, rusanov_flux, create_rusanov_flux + + type, extends(flux_calculator_base) :: rusanov_flux + real(real64) :: wave_speed_default = 1.0_real64 + contains + procedure :: info => rusanov_info + end type rusanov_flux + + ! 构造函数接口 + interface rusanov_flux + module procedure create_rusanov_flux + end interface + +contains + + ! 构造函数 + type(rusanov_flux) function create_rusanov_flux() result(this) + this%name = "Rusanov" + this%wave_speed_default = 1.0_real64 + end function create_rusanov_flux + + subroutine rusanov_info(this) + class(rusanov_flux), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Flux calculator information:" + call this%print_basic_info() + + ! 添加Rusanov特有信息 + print *, " Type: Rusanov flux" + print *, " Default wave speed: ", this%wave_speed_default + end subroutine rusanov_info + +end module rusanov_flux_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/reconstructor/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/reconstructor/CMakeLists.txt new file mode 100644 index 00000000..c88ea647 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/reconstructor/CMakeLists.txt @@ -0,0 +1,23 @@ +# src/numerics/reconstructor/CMakeLists.txt +message(STATUS "Configuring reconstructor module...") + +# Start with just the base module +add_library(reconstructor STATIC + base.f90 + eno.f90 + weno3.f90 + weno5.f90 +) + +target_link_libraries(reconstructor PRIVATE core infrastructure) + +target_include_directories(reconstructor PUBLIC + ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +install(TARGETS reconstructor + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +message(STATUS "Reconstructor module configured") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/reconstructor/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/reconstructor/base.f90 new file mode 100644 index 00000000..53798d02 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/reconstructor/base.f90 @@ -0,0 +1,33 @@ +!src/numerics/reconstructor/base.f90 +module reconstructor_base_module + use, intrinsic :: iso_fortran_env, only: real64 + implicit none + + private + public :: real64, reconstructor_base + + ! Base reconstructor type + type :: reconstructor_base + integer :: order = 1 + character(len=20) :: name = "Base" + contains + procedure :: info => reconstructor_info + procedure :: print_basic_info => reconstructor_print_basic ! 添加一个辅助方法 + end type reconstructor_base + +contains + + subroutine reconstructor_info(this) + class(reconstructor_base), intent(in) :: this + print *, "Reconstructor information:" + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_info + + subroutine reconstructor_print_basic(this) + class(reconstructor_base), intent(in) :: this + print *, " Name: ", trim(this%name) + print *, " Order: ", this%order + end subroutine reconstructor_print_basic + +end module reconstructor_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/reconstructor/eno.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/reconstructor/eno.f90 new file mode 100644 index 00000000..f973e8b3 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/reconstructor/eno.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/eno.f90 +module eno_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, eno_reconstructor, create_eno_reconstructor ! ← 添加这个 + + type, extends(reconstructor_base) :: eno_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => eno_info + end type eno_reconstructor + + ! 构造函数接口 + interface eno_reconstructor + module procedure create_eno_reconstructor + end interface + +contains + + ! 构造函数 + type(eno_reconstructor) function create_eno_reconstructor() result(this) + this%name = "ENO" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_eno_reconstructor + + subroutine eno_info(this) + class(eno_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加ENO特有信息 + print *, " Type: ENO reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine eno_info + +end module eno_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/reconstructor/weno3.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/reconstructor/weno3.f90 new file mode 100644 index 00000000..a8faa577 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/reconstructor/weno3.f90 @@ -0,0 +1,40 @@ +! src/numerics/reconstructor/weno3.f90 +module weno3_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + private + + type, extends(reconstructor_base), public :: weno3_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno3_info + end type weno3_reconstructor + + ! 构造函数接口 - 使用类型名本身作为构造函数 + interface weno3_reconstructor + module procedure create_weno3_reconstructor + end interface + +contains + + ! 构造函数 + type(weno3_reconstructor) function create_weno3_reconstructor() result(this) + this%name = "WENO3" + this%order = 3 + this%epsilon = 1.0e-6_real64 + end function create_weno3_reconstructor + + subroutine weno3_info(this) + class(weno3_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO3特有信息 + print *, " Type: WENO-3 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno3_info + +end module weno3_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/reconstructor/weno5.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/reconstructor/weno5.f90 new file mode 100644 index 00000000..a869c67d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/reconstructor/weno5.f90 @@ -0,0 +1,42 @@ +! src/numerics/reconstructor/weno5.f90(新增) +module weno5_reconstructor_module + use, intrinsic :: iso_fortran_env, only: real64 + use reconstructor_base_module, only: reconstructor_base + implicit none + + private + public :: real64, weno5_reconstructor, create_weno5_reconstructor + + type, extends(reconstructor_base) :: weno5_reconstructor + real(real64) :: epsilon = 1.0e-6_real64 + contains + procedure :: info => weno5_info + end type weno5_reconstructor + + ! 构造函数接口 + interface weno5_reconstructor + module procedure create_weno5_reconstructor + end interface + +contains + + ! 构造函数 + type(weno5_reconstructor) function create_weno5_reconstructor() result(this) + this%name = "WENO5" + this%order = 5 + this%epsilon = 1.0e-6_real64 + end function create_weno5_reconstructor + + subroutine weno5_info(this) + class(weno5_reconstructor), intent(in) :: this + + ! 调用父类的基础信息打印 + print *, "Reconstructor information:" + call this%print_basic_info() + + ! 添加WENO5特有信息 + print *, " Type: WENO-5 reconstructor" + print *, " Epsilon: ", this%epsilon + end subroutine weno5_info + +end module weno5_reconstructor_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/time_integration/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/time_integration/CMakeLists.txt new file mode 100644 index 00000000..2938656b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/time_integration/CMakeLists.txt @@ -0,0 +1,24 @@ +# src/numerics/time_integration/CMakeLists.txt +message(STATUS "配置时间积分模块...") + +add_library(time_integration STATIC + base.f90 + rk1.f90 + rk2.f90 + rk3.f90 + factory.f90 +) + +target_link_libraries(time_integration + PRIVATE + infrastructure + base + manager + solver +) + +set_target_properties(time_integration PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "时间积分模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/time_integration/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/time_integration/base.f90 new file mode 100644 index 00000000..8f010e92 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/time_integration/base.f90 @@ -0,0 +1,77 @@ +! src/numerics/time_integration/base.f90 +module time_integration_base_module + use base_modules, only: wp, ip + use config_module, only: cfd_config + use domain_module, only: domain_type + use solution_module, only: solution_type + use residual_module, only: residual_calculator + use boundary_base_module, only: boundary_condition + + implicit none + private + + type, abstract, public :: time_integrator_base + type(cfd_config), pointer :: config => null() + type(domain_type), pointer :: domain => null() + type(solution_type), pointer :: solution => null() + type(residual_calculator), pointer :: residual_calc => null() + class(boundary_condition), pointer :: bc => null() + contains + procedure :: initialize => integrator_initialize + procedure(step_interface), deferred :: step + procedure :: compute_residual => integrator_compute_residual + procedure :: apply_boundary => integrator_apply_boundary + procedure :: map_idx => integrator_map_idx + end type time_integrator_base + + abstract interface + subroutine step_interface(this, dt) + import :: time_integrator_base, wp + class(time_integrator_base), intent(inout) :: this + real(wp), intent(in) :: dt + end subroutine step_interface + end interface + +contains + + subroutine integrator_initialize(this, config, domain, solution, residual_calc, bc) + class(time_integrator_base), intent(inout) :: this + type(cfd_config), target, intent(in) :: config + type(domain_type), target, intent(in) :: domain + type(solution_type), target, intent(in) :: solution + type(residual_calculator), target, intent(in) :: residual_calc + class(boundary_condition), target, intent(in) :: bc + + this%config => config + this%domain => domain + this%solution => solution + this%residual_calc => residual_calc + this%bc => bc + end subroutine integrator_initialize + + subroutine integrator_compute_residual(this) + class(time_integrator_base), intent(inout) :: this + + if (associated(this%residual_calc) .and. associated(this%solution)) then + call this%residual_calc%compute(this%solution%u) + end if + end subroutine integrator_compute_residual + + subroutine integrator_apply_boundary(this) + class(time_integrator_base), intent(inout) :: this + + if (associated(this%bc) .and. associated(this%solution)) then + call this%bc%apply(this%solution%u, & + this%domain%nghosts, & + this%domain%ist, & + this%domain%ied - 1) + end if + end subroutine integrator_apply_boundary + + integer function integrator_map_idx(this, i) result(idx) + class(time_integrator_base), intent(in) :: this + integer(ip), intent(in) :: i + idx = i - this%domain%ist + 1 + end function integrator_map_idx + +end module time_integration_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/time_integration/base_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/time_integration/base_simple.f90 new file mode 100644 index 00000000..3631e65b --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/time_integration/base_simple.f90 @@ -0,0 +1,53 @@ +! src/numerics/time_integration/base_simple.f90 +module time_integration_base_simple_module + use base_modules, only: wp, ip + use config_module, only: cfd_config + use domain_module, only: domain_type + use residual_simple_module, only: residual_calculator_simple + + implicit none + private + + type, abstract, public :: time_integrator_base_simple + type(cfd_config), pointer :: config => null() + type(domain_type), pointer :: domain => null() + type(residual_calculator_simple), pointer :: residual_calc => null() + contains + procedure :: initialize => integrator_simple_initialize + procedure(step_interface), deferred :: step + procedure :: compute_residual => integrator_simple_compute_residual + end type time_integrator_base_simple + + abstract interface + subroutine step_interface(this, u, dt) + import :: time_integrator_base_simple, wp + class(time_integrator_base_simple), intent(inout) :: this + real(wp), intent(inout) :: u(:) + real(wp), intent(in) :: dt + end subroutine step_interface + end interface + +contains + + subroutine integrator_simple_initialize(this, config, domain, residual_calc) + class(time_integrator_base_simple), intent(inout) :: this + type(cfd_config), target, intent(in) :: config + type(domain_type), target, intent(in) :: domain + type(residual_calculator_simple), target, intent(in) :: residual_calc + + this%config => config + this%domain => domain + this%residual_calc => residual_calc + end subroutine integrator_simple_initialize + + subroutine integrator_simple_compute_residual(this, u, res) + class(time_integrator_base_simple), intent(inout) :: this + real(wp), intent(in) :: u(:) + real(wp), intent(out) :: res(:) + + if (associated(this%residual_calc)) then + call this%residual_calc%compute(u, res) + end if + end subroutine integrator_simple_compute_residual + +end module time_integration_base_simple_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/time_integration/factory.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/time_integration/factory.f90 new file mode 100644 index 00000000..4b454e44 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/time_integration/factory.f90 @@ -0,0 +1,51 @@ +! src/numerics/time_integration/factory.f90 +module time_integrator_factory_module + use base_modules, only: wp, ip + use time_integration_base_module, only: time_integrator_base + use rk1_integrator_module, only: rk1_integrator + use rk2_integrator_module, only: rk2_integrator + use rk3_integrator_module, only: rk3_integrator + + implicit none + private + + public :: create_time_integrator + +contains + + function create_time_integrator(rk_order, config, domain, solution, residual_calc, bc) & + result(integrator) + integer, intent(in) :: rk_order + type(cfd_config), target, intent(in) :: config + type(domain_type), target, intent(in) :: domain + type(solution_type), target, intent(in) :: solution + type(residual_calculator), target, intent(in) :: residual_calc + class(boundary_condition), target, intent(in) :: bc + class(time_integrator_base), allocatable :: integrator + + select case (rk_order) + case (1) + allocate(rk1_integrator :: integrator) + + case (2) + allocate(rk2_integrator :: integrator) + + case (3) + allocate(rk3_integrator :: integrator) + + case default + if (config%verbose) then + print *, "[WARNING] Unsupported RK order: ", rk_order, ", using RK2 as default" + end if + allocate(rk2_integrator :: integrator) + end select + + ! 初始化积分器 + call integrator%initialize(config, domain, solution, residual_calc, bc) + + if (config%verbose) then + print *, "[TIME INTEGRATION] Created RK", rk_order, " integrator" + end if + end function create_time_integrator + +end module time_integrator_factory_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/time_integration/rk1.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/time_integration/rk1.f90 new file mode 100644 index 00000000..750f7858 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/time_integration/rk1.f90 @@ -0,0 +1,43 @@ +! src/numerics/time_integration/rk1.f90 +module rk1_integrator_module + use base_modules, only: wp, ip + use time_integration_base_module, only: time_integrator_base + use solution_module, only: update_old_field + + implicit none + private + + type, extends(time_integrator_base), public :: rk1_integrator + contains + procedure :: step => rk1_step + end type rk1_integrator + +contains + + subroutine rk1_step(this, dt) + class(rk1_integrator), intent(inout) :: this + real(wp), intent(in) :: dt + + integer(ip) :: i, idx + + ! 计算残差 + call this%compute_residual() + + ! 更新解 (一阶RK = 欧拉方法) + do i = this%domain%ist, this%domain%ied - 1 + idx = this%map_idx(i) + this%solution%u(i) = this%solution%u(i) + dt * this%solution%res(idx) + end do + + ! 应用边界条件 + call this%apply_boundary() + + ! 更新旧场 + call this%solution%update_old_field() + + if (this%config%verbose .and. mod(this%solution%current_step, 100) == 0) then + print *, " [RK1] Step completed" + end if + end subroutine rk1_step + +end module rk1_integrator_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/time_integration/rk2.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/time_integration/rk2.f90 new file mode 100644 index 00000000..c329cbaa --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/time_integration/rk2.f90 @@ -0,0 +1,76 @@ +! src/numerics/time_integration/rk2.f90 +module rk2_integrator_module + use base_modules, only: wp, ip + use time_integration_base_module, only: time_integrator_base + use solution_module, only: update_old_field + + implicit none + private + + type, extends(time_integrator_base), public :: rk2_integrator + contains + procedure :: step => rk2_step + end type rk2_integrator + +contains + + subroutine rk2_step(this, dt) + class(rk2_integrator), intent(inout) :: this + real(wp), intent(in) :: dt + + integer(ip) :: i, idx + real(wp), allocatable :: u_pred(:) + integer(ip) :: n_cells + + ! 获取物理区域大小 + n_cells = this%domain%ied - this%domain%ist + + ! 分配预测解数组 + allocate(u_pred(this%domain%ist:this%domain%ied - 1)) + + ! 阶段1: 计算预测值 + call this%compute_residual() + do i = this%domain%ist, this%domain%ied - 1 + idx = this%map_idx(i) + u_pred(i) = this%solution%u(i) + dt * this%solution%res(idx) + end do + + ! 应用边界条件到预测值 + if (associated(this%bc)) then + call this%bc%apply(u_pred, & + this%domain%nghosts, & + this%domain%ist, & + this%domain%ied - 1) + end if + + ! 阶段2: 计算修正值 + ! 将预测值复制回当前解 + this%solution%u(this%domain%ist:this%domain%ied - 1) = & + u_pred(this%domain%ist:this%domain%ied - 1) + + ! 再次计算残差 + call this%compute_residual() + + ! 计算最终值 (Heun方法: u^{n+1} = 0.5*u^n + 0.5*(u_pred + dt*res(u_pred))) + do i = this%domain%ist, this%domain%ied - 1 + idx = this%map_idx(i) + this%solution%u(i) = 0.5_wp * this%solution%un(i) + & + 0.5_wp * this%solution%u(i) + & + 0.5_wp * dt * this%solution%res(idx) + end do + + ! 应用边界条件 + call this%apply_boundary() + + ! 更新旧场 + call this%solution%update_old_field() + + ! 清理 + deallocate(u_pred) + + if (this%config%verbose .and. mod(this%solution%current_step, 100) == 0) then + print *, " [RK2] Step completed" + end if + end subroutine rk2_step + +end module rk2_integrator_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/time_integration/rk3.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/time_integration/rk3.f90 new file mode 100644 index 00000000..b84b970d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/numerics/time_integration/rk3.f90 @@ -0,0 +1,98 @@ +! src/numerics/time_integration/rk3.f90 +module rk3_integrator_module + use base_modules, only: wp, ip + use time_integration_base_module, only: time_integrator_base + use solution_module, only: update_old_field + + implicit none + private + + type, extends(time_integrator_base), public :: rk3_integrator + contains + procedure :: step => rk3_step + end type rk3_integrator + +contains + + subroutine rk3_step(this, dt) + class(rk3_integrator), intent(inout) :: this + real(wp), intent(in) :: dt + + integer(ip) :: i, idx + real(wp), allocatable :: u1(:), u2(:) + integer(ip) :: n_cells + + ! 获取物理区域大小 + n_cells = this%domain%ied - this%domain%ist + + ! 分配中间解数组 + allocate(u1(this%domain%ist:this%domain%ied - 1)) + allocate(u2(this%domain%ist:this%domain%ied - 1)) + + ! === 阶段1 === + call this%compute_residual() + do i = this%domain%ist, this%domain%ied - 1 + idx = this%map_idx(i) + u1(i) = this%solution%u(i) + dt * this%solution%res(idx) + end do + + ! 应用边界条件到u1 + if (associated(this%bc)) then + call this%bc%apply(u1, & + this%domain%nghosts, & + this%domain%ist, & + this%domain%ied - 1) + end if + + ! === 阶段2 === + ! 将u1复制回当前解 + this%solution%u(this%domain%ist:this%domain%ied - 1) = & + u1(this%domain%ist:this%domain%ied - 1) + + call this%compute_residual() + + do i = this%domain%ist, this%domain%ied - 1 + idx = this%map_idx(i) + u2(i) = 0.75_wp * this%solution%un(i) + & + 0.25_wp * this%solution%u(i) + & + 0.25_wp * dt * this%solution%res(idx) + end do + + ! 应用边界条件到u2 + if (associated(this%bc)) then + call this%bc%apply(u2, & + this%domain%nghosts, & + this%domain%ist, & + this%domain%ied - 1) + end if + + ! === 阶段3 === + ! 将u2复制回当前解 + this%solution%u(this%domain%ist:this%domain%ied - 1) = & + u2(this%domain%ist:this%domain%ied - 1) + + call this%compute_residual() + + ! TVD RK3系数 + do i = this%domain%ist, this%domain%ied - 1 + idx = this%map_idx(i) + this%solution%u(i) = (1.0_wp/3.0_wp) * this%solution%un(i) + & + (2.0_wp/3.0_wp) * this%solution%u(i) + & + (2.0_wp/3.0_wp) * dt * this%solution%res(idx) + end do + + ! 最终边界条件 + call this%apply_boundary() + + ! 更新旧场 + call this%solution%update_old_field() + + ! 清理 + deallocate(u1, u2) + + if (this%config%verbose .and. mod(this%solution%current_step, 100) == 0) then + print *, " [RK3] Step completed" + end if + end subroutine rk3_step + +end module rk3_integrator_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/physics/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03g/src/physics/CMakeLists.txt new file mode 100644 index 00000000..cc4e233a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/physics/CMakeLists.txt @@ -0,0 +1,19 @@ +# src/physics/CMakeLists.txt +message(STATUS "配置物理模块...") + +# 创建物理模块库 +add_library(physics STATIC + physics_interface.f90 + equations/linear_convection.f90 + problems/linear_convection_problem.f90 +) + +# 链接依赖 +target_link_libraries(physics PRIVATE base) + +# 设置模块输出目录 +set_target_properties(physics PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "物理模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/physics/equations/linear_convection.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/physics/equations/linear_convection.f90 new file mode 100644 index 00000000..3be3b8f7 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/physics/equations/linear_convection.f90 @@ -0,0 +1,43 @@ +! src/physics/equations/linear_advection.f90 +module linear_advection_equation + use base_modules, only: wp, ip + implicit none + private + + type, public :: linear_advection_eq + real(wp) :: wave_speed = 1.0_wp + contains + procedure :: flux => eq_flux + procedure :: max_wave_speed => eq_max_wave_speed + procedure :: num_eqs => eq_num_eqs + procedure :: print_info => eq_print_info + end type + +contains + + pure function eq_flux(this, u) result(f) + class(linear_advection_eq), intent(in) :: this + real(wp), intent(in) :: u + real(wp) :: f + f = this%wave_speed * u + end function + + pure function eq_max_wave_speed(this, u) result(smax) + class(linear_advection_eq), intent(in) :: this + real(wp), intent(in) :: u + real(wp) :: smax + smax = abs(this%wave_speed) + end function + + integer function eq_num_eqs(this) + class(linear_advection_eq), intent(in) :: this + eq_num_eqs = 1 + end function + + subroutine eq_print_info(this) + class(linear_advection_eq), intent(in) :: this + print *, "Linear Advection Equation:" + print *, " Wave speed: ", this%wave_speed + end subroutine + +end module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/physics/physics_interface.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/physics/physics_interface.f90 new file mode 100644 index 00000000..45002da6 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/physics/physics_interface.f90 @@ -0,0 +1,64 @@ +! src/physics/physics_interface.f90 +module physics_interface + use precision_module, only: wp, ip + implicit none + private + + ! 定义抽象基类型 - 先声明为私有,然后在公开部分导出 + type, abstract :: physics_equation + character(len=:), allocatable :: name + contains + procedure(eq_flux_abs), deferred :: flux + procedure(eq_speed_abs), deferred :: speed + end type physics_equation + + type, abstract :: physics_problem + character(len=:), allocatable :: name + contains + procedure(prob_ic_abs), deferred :: initial_condition + procedure(prob_bc_abs), deferred :: boundary_condition + procedure(prob_exact_abs), deferred :: exact_solution + end type physics_problem + + ! 抽象接口定义 + abstract interface + pure function eq_flux_abs(this, u) result(f) + import :: physics_equation, wp + class(physics_equation), intent(in) :: this + real(wp), intent(in) :: u + real(wp) :: f + end function eq_flux_abs + + pure function eq_speed_abs(this) result(a) + import :: physics_equation, wp + class(physics_equation), intent(in) :: this + real(wp) :: a + end function eq_speed_abs + + subroutine prob_ic_abs(this, x, u) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp), intent(out) :: u(:) + end subroutine prob_ic_abs + + subroutine prob_bc_abs(this, u, t) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(inout) :: u(:) + real(wp), intent(in), optional :: t + end subroutine prob_bc_abs + + function prob_exact_abs(this, x, t) result(u) + import :: physics_problem, wp + class(physics_problem), intent(in) :: this + real(wp), intent(in) :: x(:), t + real(wp), dimension(size(x)) :: u + end function prob_exact_abs + end interface + + ! 公开接口 - 使用独立的public语句 + public :: wp, ip + public :: physics_equation, physics_problem + +end module physics_interface \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/physics/problems/linear_advection_problem.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/physics/problems/linear_advection_problem.f90 new file mode 100644 index 00000000..419d57cf --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/physics/problems/linear_advection_problem.f90 @@ -0,0 +1,70 @@ +! src/physics/problems/linear_advection_problem.f90 +module linear_advection_problem + use base_modules, only: wp, ip + use initial_condition_module, only: create_initial_condition + use boundary_condition_module, only: create_boundary_condition + implicit none + private + + type, public :: linear_advection_problem + character(len=20) :: ic_type = "step" + character(len=20) :: boundary_type = "periodic" + real(wp) :: left_value = 1.0_wp + real(wp) :: right_value = 2.0_wp + real(wp) :: domain_length = 2.0_wp + real(wp) :: wave_speed = 1.0_wp + contains + procedure :: create_ic => prob_create_ic + procedure :: create_bc => prob_create_bc + procedure :: exact_solution => prob_exact_solution + end type + +contains + + function prob_create_ic(this, config) result(ic) + class(linear_advection_problem), intent(in) :: this + class(*), intent(in) :: config + class(*), allocatable :: ic + + ! 通过初始条件工厂创建 + call create_initial_condition(this%ic_type, config, ic) + end function + + function prob_create_bc(this, cfd) result(bc) + class(linear_advection_problem), intent(in) :: this + class(*), intent(in) :: cfd + class(*), allocatable :: bc + + ! 通过边界条件工厂创建 + call create_boundary_condition(this%boundary_type, cfd, bc) + end function + + function prob_exact_solution(this, x, t) result(u) + class(linear_advection_problem), intent(in) :: this + real(wp), intent(in) :: x(:), t + real(wp), allocatable :: u(:) + + integer :: i, n + real(wp) :: x_shifted, L + + n = size(x) + L = this%domain_length + allocate(u(n)) + + ! 周期性平移 + do i = 1, n + x_shifted = x(i) - this%wave_speed * t + x_shifted = modulo(x_shifted, L) + if (x_shifted < 0.0_wp) x_shifted = x_shifted + L + + ! 初始条件逻辑(简化) + if (this%ic_type == "step" .and. & + x_shifted >= 0.5_wp .and. x_shifted <= 1.0_wp) then + u(i) = 2.0_wp + else + u(i) = 1.0_wp + end if + end do + end function + +end module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/physics/problems/linear_convection_problem.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/physics/problems/linear_convection_problem.f90 new file mode 100644 index 00000000..06226ed1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/physics/problems/linear_convection_problem.f90 @@ -0,0 +1,118 @@ +! src/physics/problems/linear_convection_problem.f90 +module linear_convection_problem + use precision_module, only: wp, ip + use physics_interface, only: physics_problem + implicit none + private + + ! 具体问题类型 - 先声明 + type, extends(physics_problem) :: linear_convection_prob + real(wp) :: wave_speed = 1.0_wp + real(wp) :: domain_length = 2.0_wp + character(len=20) :: ic_type = "step" + character(len=20) :: boundary_type = "periodic" + contains + procedure :: initial_condition => lc_initial_condition + procedure :: boundary_condition => lc_boundary_condition + procedure :: exact_solution => lc_exact_solution + end type linear_convection_prob + + ! 公开接口 + public :: wp, ip + public :: linear_convection_prob, create_linear_convection_prob + +contains + + ! 构造函数 + function create_linear_convection_prob(wave_speed, domain_length, & + ic_type, boundary_type) result(prob) + real(wp), intent(in), optional :: wave_speed, domain_length + character(len=*), intent(in), optional :: ic_type, boundary_type + type(linear_convection_prob) :: prob + + prob%name = "Linear Convection Problem" + + if (present(wave_speed)) prob%wave_speed = wave_speed + if (present(domain_length)) prob%domain_length = domain_length + if (present(ic_type)) prob%ic_type = ic_type + if (present(boundary_type)) prob%boundary_type = boundary_type + end function create_linear_convection_prob + + ! 初始条件 + subroutine lc_initial_condition(this, x, u) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(in) :: x(:) + real(wp), intent(out) :: u(:) + + integer :: i + + select case (trim(this%ic_type)) + case ("step") + do i = 1, size(x) + if (x(i) >= 0.5_wp .and. x(i) <= 1.0_wp) then + u(i) = 2.0_wp + else + u(i) = 1.0_wp + end if + end do + + case ("sin", "sine") + do i = 1, size(x) + u(i) = sin(2.0_wp * 3.141592653589793_wp * x(i) / this%domain_length) + end do + + case ("gaussian") + do i = 1, size(x) + u(i) = exp(-((x(i) - 0.5_wp) / 0.1_wp)**2) + end do + + case default + ! 默认阶跃函数 + do i = 1, size(x) + if (x(i) >= 0.5_wp .and. x(i) <= 1.0_wp) then + u(i) = 2.0_wp + else + u(i) = 1.0_wp + end if + end do + end select + end subroutine lc_initial_condition + + ! 边界条件(虚拟实现,实际在boundary模块) + subroutine lc_boundary_condition(this, u, t) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(inout) :: u(:) + real(wp), intent(in), optional :: t + + ! 边界条件将在独立模块实现 + print *, "[PROBLEM] Boundary condition placeholder" + if (present(t)) then + print *, " Time = ", t + end if + end subroutine lc_boundary_condition + + ! 精确解(周期性平移) + function lc_exact_solution(this, x, t) result(u) + class(linear_convection_prob), intent(in) :: this + real(wp), intent(in) :: x(:), t + real(wp), dimension(size(x)) :: u + real(wp), dimension(size(x)) :: x_shifted + integer :: i + + ! 周期性平移 + do i = 1, size(x) + x_shifted(i) = x(i) - this%wave_speed * t + ! 确保在 [0, domain_length) 范围内 + do while (x_shifted(i) < 0.0_wp) + x_shifted(i) = x_shifted(i) + this%domain_length + end do + do while (x_shifted(i) >= this%domain_length) + x_shifted(i) = x_shifted(i) - this%domain_length + end do + end do + + ! 重用初始条件函数 + call this%initial_condition(x_shifted, u) + end function lc_exact_solution + +end module linear_convection_problem \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/results.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/results.f90 new file mode 100644 index 00000000..f7ce0a7a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/results.f90 @@ -0,0 +1,290 @@ +! src/results.f90 (修正版) +module results_module + use base_modules, only: wp, ip + use config_module, only: cfd_config + use mesh_module, only: mesh_type + use domain_module, only: domain_type + use solution_module, only: solution_type + ! use physics_solver_module, only: physics_solver ! 暂时注释掉,避免循环依赖 + + implicit none + private + public :: results_saver, results_saver_create, save_results + + ! 定义字符串长度常量 + integer, parameter :: STR_LEN = 128 + + ! 结果类型 - 对应Julia的result字典 + type :: cfd_results + character(len=STR_LEN) :: solver_name = "" + real(wp), allocatable :: x(:) ! 网格坐标(单元中心) + real(wp), allocatable :: numerical(:) ! 数值解 + real(wp), allocatable :: analytical(:) ! 解析解 + character(len=STR_LEN) :: scheme = "" ! 格式名称 + integer :: order = 0 ! 阶数 + integer :: rk_order = 0 ! RK阶数 + real(wp) :: final_time = 0.0_wp ! 最终时间 + real(wp) :: current_time = 0.0_wp ! 当前时间 + integer :: total_steps = 0 ! 总步数 + integer :: solver_state = 0 ! 求解器状态 + end type cfd_results + + ! 结果保存器 + type :: results_saver + character(len=STR_LEN) :: base_filename = "results" + logical :: verbose = .true. + contains + procedure :: save_text => results_saver_save_text + procedure :: save_binary => results_saver_save_binary + procedure :: load => results_saver_load + end type results_saver + + ! 接口声明 + interface results_saver + module procedure results_saver_constructor + end interface + +contains + + ! 构造函数 + function results_saver_constructor(base_filename, verbose) result(saver) + character(len=*), optional :: base_filename + logical, optional :: verbose + type(results_saver) :: saver + + if (present(base_filename)) then + saver%base_filename = trim(adjustl(base_filename)) + end if + if (present(verbose)) then + saver%verbose = verbose + end if + end function results_saver_constructor + + ! 保持向后兼容的创建函数 + function results_saver_create(base_filename, verbose) result(saver) + character(len=*), optional :: base_filename + logical, optional :: verbose + type(results_saver) :: saver + + saver = results_saver_constructor(base_filename, verbose) + end function results_saver_create + + ! 生成文件名(与Julia风格一致) + function generate_filename(saver, solver_name, mesh_size) result(filename) + class(results_saver), intent(in) :: saver + character(len=*), intent(in) :: solver_name + integer, intent(in) :: mesh_size + character(len=STR_LEN) :: filename + + write(filename, '(A, "_", A, "_", I0, ".dat")') & + trim(saver%base_filename), trim(solver_name), mesh_size + end function generate_filename + + ! 主保存函数 - 生成与Julia兼容的结果 + subroutine save_results(saver, solver_name, config, mesh, domain, solution, & + current_time, total_steps, solver_state) + class(results_saver), intent(in) :: saver + character(len=*), intent(in) :: solver_name + type(cfd_config), intent(in) :: config + type(mesh_type), intent(in) :: mesh + type(domain_type), intent(in) :: domain + type(solution_type), intent(in) :: solution + real(wp), intent(in) :: current_time + integer, intent(in) :: total_steps, solver_state + + type(cfd_results) :: results + character(len=STR_LEN) :: filename + integer :: i, n_physical + + ! 准备结果数据 + results%solver_name = trim(solver_name) + results%scheme = trim(config%recon_scheme) + results%order = config%spatial_order + results%rk_order = config%rk_order + results%final_time = config%final_time + results%current_time = current_time + results%total_steps = total_steps + results%solver_state = solver_state + + ! 分配数组 + n_physical = mesh%ncells + allocate(results%x(n_physical)) + allocate(results%numerical(n_physical)) + allocate(results%analytical(n_physical)) + + ! 填充网格坐标(单元中心) + results%x = mesh%xcc + + ! 填充数值解(仅物理区域) + do i = 1, n_physical + results%numerical(i) = solution%u(domain%ist + i - 1) + end do + + ! 生成解析解(与Julia的exact_solution对应) + call generate_analytical_solution(results%x, config, results%analytical, current_time) + + ! 生成文件名 + filename = generate_filename(saver, solver_name, mesh%ncells) + + ! 保存文件 + if (saver%verbose) then + print *, "[RESULTS] Saving results to: ", trim(filename) + print *, " Solver: ", trim(results%solver_name) + print *, " Scheme: ", trim(results%scheme), " order ", results%order + print *, " Time: ", results%current_time, " / ", results%final_time + print *, " Steps: ", results%total_steps + end if + + call saver%save_text(results, filename) + + ! 清理 + deallocate(results%x, results%numerical, results%analytical) + end subroutine save_results + + ! 生成解析解(匹配Julia的exact_solution逻辑) + subroutine generate_analytical_solution(x, config, analytical, current_time) + real(wp), intent(in) :: x(:), current_time + type(cfd_config), intent(in) :: config + real(wp), intent(out) :: analytical(:) + + integer :: i, n + real(wp) :: x_shifted, L + + n = size(x) + L = config%domain_length + + select case (trim(config%ic_type)) + case ("step") + ! 阶跃函数的精确解(周期性) + do i = 1, n + ! 周期性平移 + x_shifted = x(i) - config%wave_speed * current_time + x_shifted = modulo(x_shifted, L) + if (x_shifted < 0.0_wp) x_shifted = x_shifted + L + + ! 阶跃在 [0.5, 1.0] 内为 2.0,其他为 1.0 + if (x_shifted >= 0.5_wp .and. x_shifted <= 1.0_wp) then + analytical(i) = 2.0_wp + else + analytical(i) = 1.0_wp + end if + end do + + case ("sin", "sine") + ! 正弦波的精确解 + do i = 1, n + x_shifted = x(i) - config%wave_speed * current_time + x_shifted = modulo(x_shifted, L) + analytical(i) = sin(2.0_wp * 3.141592653589793_wp * x_shifted / L) + end do + + case ("gaussian") + ! 高斯脉冲的精确解 + do i = 1, n + x_shifted = x(i) - config%wave_speed * current_time + x_shifted = modulo(x_shifted, L) + analytical(i) = exp(-50.0_wp * (x_shifted - 1.0_wp)**2) + end do + + case default + ! 默认:阶跃函数 + do i = 1, n + x_shifted = x(i) - config%wave_speed * current_time + x_shifted = modulo(x_shifted, L) + if (x_shifted >= 0.5_wp .and. x_shifted <= 1.0_wp) then + analytical(i) = 2.0_wp + else + analytical(i) = 1.0_wp + end if + end do + end select + end subroutine generate_analytical_solution + + ! 文本格式保存(与Julia的纯文本输出兼容) + subroutine results_saver_save_text(this, results, filename) + class(results_saver), intent(in) :: this + type(cfd_results), intent(in) :: results + character(len=*), intent(in) :: filename + + integer :: i, n, unit, ierr + + n = size(results%x) + + ! 打开文件 + open(newunit=unit, file=trim(filename), status='replace', & + action='write', iostat=ierr) + + if (ierr /= 0) then + if (this%verbose) then + print *, "[ERROR] Cannot open file: ", trim(filename) + end if + return + end if + + ! 写入头部信息(类似Julia的输出格式) + write(unit, '(A)') "========================================" + write(unit, '(A)') "CFD SOLVER RESULTS (Fortran)" + write(unit, '(A)') "========================================" + write(unit, '(A, A)') "Solver: ", trim(results%solver_name) + write(unit, '(A, A)') "Scheme: ", trim(results%scheme) + write(unit, '(A, I0)') "Order: ", results%order + write(unit, '(A, I0)') "RK Order: ", results%rk_order + write(unit, '(A, ES15.8)') "Final Time: ", results%final_time + write(unit, '(A, ES15.8)') "Current Time: ", results%current_time + write(unit, '(A, I0)') "Total Steps: ", results%total_steps + write(unit, '(A, I0)') "Solver State: ", results%solver_state + write(unit, '(A, I0)') "Grid Points: ", n + write(unit, '(A)') "========================================" + write(unit, '(A)') "DATA: x, numerical, analytical" + write(unit, '(A)') "========================================" + + ! 写入数据 + do i = 1, n + write(unit, '(3ES20.12)') results%x(i), results%numerical(i), results%analytical(i) + end do + + ! 关闭文件 + close(unit) + + if (this%verbose) then + print *, "[RESULTS] Saved ", n, " data points to ", trim(filename) + end if + end subroutine results_saver_save_text + + ! 二进制保存(可选) + subroutine results_saver_save_binary(this, results, filename) + class(results_saver), intent(in) :: this + type(cfd_results), intent(in) :: results + character(len=*), intent(in) :: filename + + ! 暂时实现文本格式,二进制格式可后续添加 + if (this%verbose) then + print *, "[INFO] Binary save not implemented, using text format" + end if + call this%save_text(results, filename) + end subroutine results_saver_save_binary + + ! 加载结果(暂时简单实现) + subroutine results_saver_load(this, filename, results) + class(results_saver), intent(in) :: this + character(len=*), intent(in) :: filename + type(cfd_results), intent(out) :: results + + ! 简化:只打印文件信息 + if (this%verbose) then + print *, "[RESULTS] Would load from: ", trim(filename) + print *, " Note: Load functionality needs implementation" + end if + + ! 初始化结果结构以避免未初始化警告 + results%solver_name = "" + results%scheme = "" + results%order = 0 + results%rk_order = 0 + results%final_time = 0.0_wp + results%current_time = 0.0_wp + results%total_steps = 0 + results%solver_state = 0 + end subroutine results_saver_load + +end module results_module diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/solver/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03g/src/solver/CMakeLists.txt new file mode 100644 index 00000000..059dc821 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/solver/CMakeLists.txt @@ -0,0 +1,44 @@ +# src/solver/CMakeLists.txt(简化版) +message(STATUS "配置求解器模块...") + +# 基础求解器库 +add_library(solver STATIC + base.f90 + physics_solver.f90 + residual_simple.f90 # 添加简化残差计算器 +) + +target_link_libraries(solver + PRIVATE + infrastructure + core + physics + manager + results + boundary + initial_condition +) + +set_target_properties(solver PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +# 简单集成求解器库(演示用) +add_library(solver_integrated STATIC + solver_integrated.f90 +) + +target_link_libraries(solver_integrated + PRIVATE + solver + infrastructure + base + boundary + initial_condition +) + +set_target_properties(solver_integrated PROPERTIES + Fortran_MODULE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} +) + +message(STATUS "求解器模块配置完成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/solver/base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/solver/base.f90 new file mode 100644 index 00000000..cfd78c47 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/solver/base.f90 @@ -0,0 +1,264 @@ +! src/solver/base.f90 +module solver_base_module + use base_modules, only: wp => wp, ip => ip ! 重命名以避免冲突 + + use config_module, only: cfd_config + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create + + implicit none + private + + ! 明确导出列表 + public :: wp, ip ! 类型参数 + public :: solver_base, create_solver_base ! 类型和构造函数 + public :: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, SOLVER_RUNNING + public :: SOLVER_COMPLETED, SOLVER_ERROR ! 状态常量 + + ! 求解器状态枚举 + integer, parameter :: SOLVER_UNINITIALIZED = 0 + integer, parameter :: SOLVER_INITIALIZED = 1 + integer, parameter :: SOLVER_RUNNING = 2 + integer, parameter :: SOLVER_COMPLETED = 3 + integer, parameter :: SOLVER_ERROR = 4 + + ! 求解器基类 + type :: solver_base + ! 基本组件 + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + + ! 状态管理 + integer :: state = SOLVER_UNINITIALIZED + character(len=100) :: error_message = "" + real(wp) :: current_time = 0.0_wp + integer(ip) :: current_step = 0 + + ! 时间控制 + real(wp) :: dt_original = 0.0_wp + contains + procedure :: initialize => solver_base_initialize + procedure :: step => solver_base_step + procedure :: run_to_time => solver_base_run_to_time + procedure :: cleanup => solver_base_cleanup + procedure :: get_state => solver_base_get_state + procedure :: get_error => solver_base_get_error + procedure :: print_info => solver_base_print_info + end type solver_base + + ! 构造函数接口 + interface solver_base + module procedure create_solver_base + end interface + +contains + + ! ==================== 构造函数 ==================== + + function create_solver_base(config, mesh) result(solver) + type(cfd_config), intent(in) :: config + type(mesh_type), intent(in) :: mesh + type(solver_base) :: solver + + solver%config = config + solver%mesh = mesh + + ! 创建域 + solver%domain = domain_create(config, mesh) + + ! 创建解 + solver%solution = solution_create(solver%domain) + + ! 保存原始时间步长 + solver%dt_original = config%dt + + if (config%verbose) then + print *, "[SOLVER] Base solver created" + print *, " Mesh cells: ", mesh%ncells + print *, " Domain total cells: ", solver%domain%ntcells + end if + end function create_solver_base + + ! ==================== 初始化 ==================== + + subroutine solver_base_initialize(this) + class(solver_base), intent(inout) :: this + + if (this%state == SOLVER_INITIALIZED) then + if (this%config%verbose) then + print *, "[SOLVER] Already initialized" + end if + return + end if + + ! 初始化解(通过配置) + ! 这里暂时简化,实际需要调用初始条件工厂 + print *, "[INFO] Base solver initialized (simplified)" + + ! 更新状态 + this%state = SOLVER_INITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + + if (this%config%verbose) then + print *, "[SOLVER] Initialized at t = ", this%current_time + end if + end subroutine solver_base_initialize + + ! ==================== 单步计算(虚方法) ==================== + + subroutine solver_base_step(this, dt) + class(solver_base), intent(inout) :: this + real(wp), intent(in) :: dt + + ! 基类中这只是虚方法,需要在子类中实现 + print *, "[INFO] Base solver step (virtual method)" + print *, " dt = ", dt + print *, " t = ", this%current_time + + ! 更新时间 + this%current_time = this%current_time + dt + this%current_step = this%current_step + 1 + + ! 简单模拟:只是更新状态 + if (this%config%verbose) then + print *, "[SOLVER] Step completed: t = ", this%current_time, & + ", step = ", this%current_step + end if + end subroutine solver_base_step + + ! ==================== 运行到指定时间 ==================== + + subroutine solver_base_run_to_time(this, final_time) + class(solver_base), intent(inout) :: this + real(wp), intent(in) :: final_time + + real(wp) :: dt, t_remaining + integer :: step_count + + if (this%state /= SOLVER_INITIALIZED) then + this%error_message = "Solver not initialized" + this%state = SOLVER_ERROR + if (this%config%verbose) then + print *, "[SOLVER BASE ERROR] Not initialized: ", trim(this%error_message) + end if + return + end if + + this%state = SOLVER_RUNNING + step_count = 0 + + if (this%config%verbose) then + print *, "[SOLVER BASE] Running from t = ", this%current_time, & + " to t = ", final_time + print *, " Time step: ", this%config%dt + end if + + do while (this%current_time < final_time - 1e-12_wp) + ! 计算时间步长 + t_remaining = final_time - this%current_time + dt = min(this%config%dt, t_remaining) + + ! 执行时间步 + call this%step(dt) + + step_count = step_count + 1 + + ! 每50步输出一次进度 + if (mod(step_count, 50) == 0 .and. this%config%verbose) then + print *, "[SOLVER BASE] Progress: t = ", this%current_time, & + " / ", final_time, " (step ", step_count, ")" + end if + end do + + ! 恢复原始时间步长 + this%config%dt = this%dt_original + + ! 更新状态 + this%state = SOLVER_COMPLETED + + if (this%config%verbose) then + print *, "[SOLVER BASE] Run completed:" + print *, " Final time: ", this%current_time + print *, " Total steps: ", this%current_step + print *, " State: ", this%state + end if + end subroutine solver_base_run_to_time + + ! ==================== 清理 ==================== + + subroutine solver_base_cleanup(this) + class(solver_base), intent(inout) :: this + + ! 重置状态 + this%state = SOLVER_UNINITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + this%error_message = "" + + if (this%config%verbose) then + print *, "[SOLVER] Cleaned up" + end if + end subroutine solver_base_cleanup + + ! ==================== 状态查询 ==================== + + function solver_base_get_state(this) result(state) + class(solver_base), intent(in) :: this + integer :: state + state = this%state + end function solver_base_get_state + + function solver_base_get_error(this) result(error_msg) + class(solver_base), intent(in) :: this + character(len=100) :: error_msg + error_msg = trim(this%error_message) + end function solver_base_get_error + + ! ==================== 信息打印 ==================== + + subroutine solver_base_print_info(this) + class(solver_base), intent(in) :: this + + character(len=20) :: state_str + + ! 状态字符串 + select case (this%state) + case (SOLVER_UNINITIALIZED) + state_str = "Uninitialized" + case (SOLVER_INITIALIZED) + state_str = "Initialized" + case (SOLVER_RUNNING) + state_str = "Running" + case (SOLVER_COMPLETED) + state_str = "Completed" + case (SOLVER_ERROR) + state_str = "Error" + case default + state_str = "Unknown" + end select + + print *, "=== Solver Information ===" + print *, "State: ", trim(state_str) + print *, "Current time: ", this%current_time + print *, "Current step: ", this%current_step + print *, "Error message: '", trim(this%error_message), "'" + + ! 配置信息 + print *, "Configuration:" + print *, " Scheme: ", trim(this%config%recon_scheme) + print *, " Order: ", this%config%spatial_order + print *, " dt: ", this%config%dt + + ! 域信息 + print *, "Domain:" + print *, " Ghost layers: ", this%domain%nghosts + print *, " Physical cells: ", this%domain%ist, " to ", this%domain%ied - 1 + + print *, "=========================" + end subroutine solver_base_print_info + +end module solver_base_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/solver/physics_solver.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/solver/physics_solver.f90 new file mode 100644 index 00000000..4582042d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/solver/physics_solver.f90 @@ -0,0 +1,188 @@ +! src/solver/physics_solver.f90 (修正版) +module physics_solver_module + use base_modules, only: wp => wp, ip => ip + use config_module, only: cfd_config + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create + + implicit none + private + + ! 明确导出列表 + public :: wp, ip, physics_solver + public :: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, SOLVER_COMPLETED, SOLVER_ERROR + + ! 求解器状态枚举 + integer, parameter :: SOLVER_UNINITIALIZED = 0 + integer, parameter :: SOLVER_INITIALIZED = 1 + integer, parameter :: SOLVER_RUNNING = 2 + integer, parameter :: SOLVER_COMPLETED = 3 + integer, parameter :: SOLVER_ERROR = 4 + + ! 物理求解器类型 + type :: physics_solver + ! 基本组件 + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + + ! 状态管理 + integer :: state = SOLVER_UNINITIALIZED + character(len=100) :: error_message = "" + real(wp) :: current_time = 0.0_wp + integer(ip) :: current_step = 0 + + ! 时间控制 + real(wp) :: dt_original = 0.0_wp + contains + procedure :: initialize => physics_solver_initialize + procedure :: run_to_time => physics_solver_run_to_time + procedure :: cleanup => physics_solver_cleanup + procedure :: get_state => physics_solver_get_state + procedure, private :: apply_simple_initial_condition ! 添加这行 + end type physics_solver + +contains + + ! ==================== 初始化 ==================== + + subroutine physics_solver_initialize(this) + class(physics_solver), intent(inout) :: this + + if (this%state == SOLVER_INITIALIZED) then + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Already initialized" + end if + return + end if + + ! 创建域 + this%domain = domain_create(this%config, this%mesh) + + ! 创建解 + this%solution = solution_create(this%domain) + + ! 应用简化的初始条件 + call this%apply_simple_initial_condition() + + ! 保存原始时间步长 + this%dt_original = this%config%dt + + ! 更新状态 + this%state = SOLVER_INITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Initialized at t = ", this%current_time + end if + end subroutine physics_solver_initialize + + subroutine apply_simple_initial_condition(this) + class(physics_solver), intent(inout) :: this + + integer :: i, idx + real(wp) :: x + + ! 简化的阶跃函数初始条件 + do i = this%domain%ist, this%domain%ied - 1 + idx = i - this%domain%ist + 1 + x = this%mesh%xcc(idx) + + if (x >= 0.5_wp .and. x <= 1.0_wp) then + this%solution%u(i) = 2.0_wp + else + this%solution%u(i) = 1.0_wp + end if + end do + + ! 同步旧场 + this%solution%un = this%solution%u + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Applied simple initial condition" + end if + end subroutine + + ! ==================== 运行到指定时间 ==================== + + subroutine physics_solver_run_to_time(this, final_time) + class(physics_solver), intent(inout) :: this + real(wp), intent(in) :: final_time + + real(wp) :: dt, t_remaining + integer :: step_count + + if (this%state /= SOLVER_INITIALIZED) then + this%error_message = "Solver not initialized" + this%state = SOLVER_ERROR + if (this%config%verbose) then + print *, "[PHYSICS SOLVER ERROR] Not initialized" + end if + return + end if + + this%state = SOLVER_RUNNING + step_count = 0 + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Running from t = ", this%current_time, & + " to t = ", final_time + end if + + do while (this%current_time < final_time - 1e-12_wp) + ! 计算时间步长 + t_remaining = final_time - this%current_time + dt = min(this%config%dt, t_remaining) + + ! 简单的时间步进(占位符) + this%current_time = this%current_time + dt + this%current_step = this%current_step + 1 + step_count = step_count + 1 + + ! 每100步输出一次进度 + if (mod(step_count, 100) == 0 .and. this%config%verbose) then + print *, "[PHYSICS SOLVER] Progress: t = ", this%current_time, & + " / ", final_time, " (step ", step_count, ")" + end if + end do + + ! 恢复原始时间步长 + this%config%dt = this%dt_original + + ! 更新状态 + this%state = SOLVER_COMPLETED + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Run completed:" + print *, " Final time: ", this%current_time + print *, " Total steps: ", this%current_step + end if + end subroutine physics_solver_run_to_time + + ! ==================== 清理 ==================== + + subroutine physics_solver_cleanup(this) + class(physics_solver), intent(inout) :: this + + this%state = SOLVER_UNINITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + this%error_message = "" + + if (this%config%verbose) then + print *, "[PHYSICS SOLVER] Cleaned up" + end if + end subroutine physics_solver_cleanup + + ! ==================== 状态查询 ==================== + + function physics_solver_get_state(this) result(state) + class(physics_solver), intent(in) :: this + integer :: state + state = this%state + end function physics_solver_get_state + +end module physics_solver_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/solver/residual.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/solver/residual.f90 new file mode 100644 index 00000000..ad0d5b8c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/solver/residual.f90 @@ -0,0 +1,152 @@ +! src/solver/residual.f90 (增强版) +module residual_module + use base_modules, only: wp, ip + use domain_module, only: domain_type + use solution_module, only: solution_type + use mesh_module, only: mesh_type + use config_module, only: cfd_config + use reconstructor_base_module, only: reconstructor_base + use flux_base_module, only: flux_calculator_base + + implicit none + private + + type, public :: residual_calculator + ! 配置和域 + type(cfd_config), pointer :: config => null() + type(mesh_type), pointer :: mesh => null() + type(domain_type), pointer :: domain => null() + + ! 数值组件 + class(reconstructor_base), pointer :: reconstructor => null() + class(flux_calculator_base), pointer :: flux_calc => null() + + ! 工作数组 + real(wp), pointer :: qL(:) => null() + real(wp), pointer :: qR(:) => null() + real(wp), pointer :: flux(:) => null() + real(wp), pointer :: res(:) => null() + + real(wp) :: dx = 0.0_wp + logical :: initialized = .false. + + contains + procedure :: initialize => residual_init + procedure :: compute => residual_compute + procedure :: cleanup => residual_cleanup + end type residual_calculator + +contains + + subroutine residual_init(this, config, mesh, domain, reconstructor, flux_calc, & + solution) + class(residual_calculator), intent(inout) :: this + type(cfd_config), target, intent(in) :: config + type(mesh_type), target, intent(in) :: mesh + type(domain_type), target, intent(in) :: domain + class(reconstructor_base), target, intent(in) :: reconstructor + class(flux_calculator_base), target, intent(in) :: flux_calc + type(solution_type), target, intent(in) :: solution + + this%config => config + this%mesh => mesh + this%domain => domain + this%reconstructor => reconstructor + this%flux_calc => flux_calc + + ! 链接工作数组 + this%qL => solution%q_face_left + this%qR => solution%q_face_right + this%flux => solution%flux + this%res => solution%res + + ! 计算网格间距 + this%dx = mesh%dx + + this%initialized = .true. + + if (config%verbose) then + print *, "[RESIDUAL] Initialized residual calculator" + print *, " Scheme: ", trim(reconstructor%name) + print *, " Flux: ", trim(flux_calc%name) + print *, " dx: ", this%dx + end if + end subroutine residual_init + + subroutine residual_compute(this, u) + class(residual_calculator), intent(inout) :: this + real(wp), intent(in) :: u(:) + + integer :: i, n_faces, n_cells + real(wp) :: f_left, f_right + + if (.not. this%initialized) then + if (this%config%verbose) then + print *, "[ERROR] Residual calculator not initialized" + end if + return + end if + + n_faces = this%mesh%nnodes + n_cells = this%mesh%ncells + + ! 阶段1: 重构界面值(简化实现) + ! 在实际实现中,这里应该调用重构器 + if (this%config%verbose .and. mod(this%domain%current_step, 100) == 0) then + print *, " [RESIDUAL] Reconstructing with ", trim(this%reconstructor%name) + end if + + ! 简化重构:线性插值 + do i = 1, n_faces - 1 + this%qL(i+1) = 0.5_wp * (u(this%domain%ist + i - 2) + & + u(this%domain%ist + i - 1)) + this%qR(i) = this%qL(i+1) + end do + + ! 周期边界处理 + this%qL(1) = 0.5_wp * (u(this%domain%ied - 1) + u(this%domain%ist)) + this%qR(n_faces) = this%qL(1) + + ! 阶段2: 计算通量(简化实现) + ! 在实际实现中,这里应该调用通量计算器 + if (this%config%verbose .and. mod(this%domain%current_step, 100) == 0) then + print *, " [RESIDUAL] Computing fluxes with ", trim(this%flux_calc%name) + end if + + ! 简化通量:Lax-Friedrichs + do i = 1, n_faces + f_left = this%config%wave_speed * this%qL(i) + f_right = this%config%wave_speed * this%qR(i) + this%flux(i) = 0.5_wp * (f_left + f_right) - & + 0.5_wp * abs(this%config%wave_speed) * (this%qR(i) - this%qL(i)) + end do + + ! 阶段3: 计算残差 + do i = 1, n_cells + this%res(i) = -(this%flux(i+1) - this%flux(i)) / this%dx + end do + + ! 调试输出 + if (this%config%verbose .and. mod(this%domain%current_step, 100) == 0) then + print *, " [RESIDUAL] Residual range: ", & + minval(this%res), " to ", maxval(this%res) + end if + end subroutine residual_compute + + subroutine residual_cleanup(this) + class(residual_calculator), intent(inout) :: this + this%initialized = .false. + + ! 清空指针 + this%config => null() + this%mesh => null() + this%domain => null() + this%reconstructor => null() + this%flux_calc => null() + this%qL => null() + this%qR => null() + this%flux => null() + this%res => null() + end subroutine residual_cleanup + +end module residual_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/solver/residual_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/solver/residual_simple.f90 new file mode 100644 index 00000000..5b8bc766 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/solver/residual_simple.f90 @@ -0,0 +1,96 @@ +! src/solver/residual_simple.f90(简化版) +module residual_simple_module + use base_modules, only: wp, ip + use config_module, only: cfd_config + use mesh_module, only: mesh_type + use domain_module, only: domain_type + + implicit none + private + + type, public :: residual_calculator_simple + ! 配置和域 + type(cfd_config), pointer :: config => null() + type(mesh_type), pointer :: mesh => null() + type(domain_type), pointer :: domain => null() + + real(wp) :: dx = 0.0_wp + logical :: initialized = .false. + + contains + procedure :: initialize => residual_simple_init + procedure :: compute => residual_simple_compute + procedure :: cleanup => residual_simple_cleanup + end type residual_calculator_simple + +contains + + subroutine residual_simple_init(this, config, mesh, domain) + class(residual_calculator_simple), intent(inout) :: this + type(cfd_config), target, intent(in) :: config + type(mesh_type), target, intent(in) :: mesh + type(domain_type), target, intent(in) :: domain + + this%config => config + this%mesh => mesh + this%domain => domain + this%dx = mesh%dx + this%initialized = .true. + + if (config%verbose) then + print *, "[RESIDUAL SIMPLE] Initialized" + print *, " dx: ", this%dx + end if + end subroutine residual_simple_init + + subroutine residual_simple_compute(this, u, res) + class(residual_calculator_simple), intent(inout) :: this + real(wp), intent(in) :: u(:) + real(wp), intent(out) :: res(:) + + integer :: i, n_cells + real(wp) :: f_left, f_right + + if (.not. this%initialized) then + if (this%config%verbose) then + print *, "[ERROR] Residual calculator not initialized" + end if + return + end if + + n_cells = this%mesh%ncells + + ! 简化的残差计算(Lax-Friedrichs格式) + do i = 1, n_cells + ! 简化的通量计算 + f_left = this%config%wave_speed * u(this%domain%ist + i - 1) + f_right = this%config%wave_speed * u(this%domain%ist + i) + + ! Lax-Friedrichs通量 + f_left = 0.5_wp * (f_left + f_right) - & + 0.5_wp * abs(this%config%wave_speed) * & + (u(this%domain%ist + i) - u(this%domain%ist + i - 1)) + + f_right = 0.5_wp * (f_left + f_right) - & + 0.5_wp * abs(this%config%wave_speed) * & + (u(this%domain%ist + i) - u(this%domain%ist + i - 1)) + + ! 残差 = -∂F/∂x + res(i) = -(f_right - f_left) / this%dx + end do + + if (this%config%verbose .and. mod(this%domain%current_step, 100) == 0) then + print *, " [RESIDUAL] Computed residual" + end if + end subroutine residual_simple_compute + + subroutine residual_simple_cleanup(this) + class(residual_calculator_simple), intent(inout) :: this + this%initialized = .false. + + this%config => null() + this%mesh => null() + this%domain => null() + end subroutine residual_simple_cleanup + +end module residual_simple_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/src/solver/solver_integrated.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/src/solver/solver_integrated.f90 new file mode 100644 index 00000000..6a9a0bfd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/src/solver/solver_integrated.f90 @@ -0,0 +1,390 @@ +! src/solver/solver_integrated.f90 (完全修正版) +module solver_integrated_module + use base_modules, only: wp, ip + use config_module, only: cfd_config + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create + use boundary_base_module, only: boundary_condition + use periodic_boundary_module, only: periodic_boundary + + implicit none + private + + ! 求解器状态常量 + integer, parameter, public :: SOLVER_READY = 0 + integer, parameter, public :: SOLVER_INITIALIZED = 1 + integer, parameter, public :: SOLVER_RUNNING = 2 + integer, parameter, public :: SOLVER_COMPLETED = 3 + integer, parameter, public :: SOLVER_ERROR = -1 + + type, public :: integrated_solver + ! 基本组件 + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + + ! 组件实例 + class(boundary_condition), allocatable :: bc + + ! 状态 + integer :: state = SOLVER_READY + real(wp) :: current_time = 0.0_wp + integer(ip) :: current_step = 0 + character(len=100) :: error_msg = "" + + ! 数据模式 + logical :: use_real_data = .true. + + contains + procedure :: initialize => solver_initialize + procedure :: run_to_time => solver_run_to_time + procedure :: cleanup => solver_cleanup + procedure :: get_state => solver_get_state + procedure :: enable_real_data => solver_enable_real_data + procedure, private :: apply_initial_condition + procedure, private :: apply_boundary_conditions + procedure, private :: simple_time_step + procedure, private :: calculate_dt + procedure, private :: setup_boundary_condition ! 修改方法名 + end type integrated_solver + +contains + + ! ==================== 公共接口 ==================== + + subroutine solver_initialize(this) + class(integrated_solver), intent(inout) :: this + + if (this%state /= SOLVER_READY) then + this%error_msg = "Solver already initialized" + this%state = SOLVER_ERROR + if (this%config%verbose) then + print *, "[ERROR] ", trim(this%error_msg) + end if + return + end if + + ! 创建域 + this%domain = domain_create(this%config, this%mesh) + + ! 创建解 + this%solution = solution_create(this%domain) + + ! 设置边界条件 + call this%setup_boundary_condition() ! 修改调用 + + ! 应用初始条件 + call this%apply_initial_condition() + + ! 初始化状态 + this%state = SOLVER_INITIALIZED + this%current_time = 0.0_wp + this%current_step = 0 + + if (this%config%verbose) then + print *, "[INTEGRATED SOLVER] Initialized successfully" + print *, " Domain cells (with ghosts): ", this%domain%ntcells + print *, " Ghost layers: ", this%domain%nghosts + if (this%use_real_data) then + print *, " Data mode: Real" + else + print *, " Data mode: Simple" + end if + end if + end subroutine solver_initialize + + ! ==================== 边界条件设置 ==================== + + subroutine setup_boundary_condition(this) + class(integrated_solver), intent(inout) :: this + + ! 根据配置创建边界条件 + select case (trim(this%config%boundary_type)) + case ("periodic") + ! 创建周期性边界条件 + if (.not. allocated(this%bc)) then + allocate(periodic_boundary :: this%bc) + end if + + select type(bc => this%bc) + type is (periodic_boundary) + bc%name = "periodic" + end select + + case default + ! 默认使用周期性边界 + if (this%config%verbose) then + print *, "[WARNING] Using periodic boundary as default" + end if + + if (.not. allocated(this%bc)) then + allocate(periodic_boundary :: this%bc) + end if + + select type(bc => this%bc) + type is (periodic_boundary) + bc%name = "periodic" + end select + end select + + if (this%config%verbose) then + print *, "[INTEGRATED SOLVER] Boundary condition created: ", & + trim(this%bc%get_name()) + end if + end subroutine setup_boundary_condition + + subroutine solver_run_to_time(this, final_time) + class(integrated_solver), intent(inout) :: this + real(wp), intent(in) :: final_time + + real(wp) :: dt, t_remaining + integer :: step_count + real(wp) :: original_dt + + if (this%state /= SOLVER_INITIALIZED) then + this%error_msg = "Solver not initialized" + this%state = SOLVER_ERROR + if (this%config%verbose) then + print *, "[ERROR] ", trim(this%error_msg) + end if + return + end if + + ! 保存原始时间步长 + original_dt = this%config%dt + + this%state = SOLVER_RUNNING + step_count = 0 + + if (this%config%verbose) then + print *, "[INTEGRATED SOLVER] Starting time integration" + print *, " Initial time: ", this%current_time + print *, " Final time: ", final_time + print *, " Initial dt: ", this%config%dt + end if + + do while (this%current_time < final_time - 1e-12_wp) + ! 计算时间步长 + call this%calculate_dt() + + ! 确保不超过最终时间 + t_remaining = final_time - this%current_time + dt = min(this%config%dt, t_remaining) + + ! 应用边界条件 + call this%apply_boundary_conditions() + + ! 执行时间步 + call this%simple_time_step(dt) + + ! 更新时间 + this%current_time = this%current_time + dt + this%current_step = this%current_step + 1 + step_count = step_count + 1 + + ! 进度输出 + if (mod(step_count, 50) == 0 .and. this%config%verbose) then + print *, " Progress: t = ", this%current_time, & + " / ", final_time, " (step ", step_count, ")" + end if + end do + + ! 恢复原始时间步长 + this%config%dt = original_dt + + ! 更新状态 + this%state = SOLVER_COMPLETED + + if (this%config%verbose) then + print *, "[INTEGRATED SOLVER] Time integration completed" + print *, " Final time: ", this%current_time + print *, " Total steps: ", this%current_step + if (allocated(this%solution%u)) then + print *, " Solution range: [", minval(this%solution%u), ", ", & + maxval(this%solution%u), "]" + end if + end if + end subroutine solver_run_to_time + + subroutine solver_cleanup(this) + class(integrated_solver), intent(inout) :: this + + ! 清理分配的组件 + if (allocated(this%bc)) then + deallocate(this%bc) + end if + + ! 重置状态 + this%state = SOLVER_READY + this%current_time = 0.0_wp + this%current_step = 0 + this%error_msg = "" + + if (this%config%verbose) then + print *, "[INTEGRATED SOLVER] Cleaned up" + end if + end subroutine solver_cleanup + + function solver_get_state(this) result(state) + class(integrated_solver), intent(in) :: this + integer :: state + state = this%state + end function solver_get_state + + subroutine solver_enable_real_data(this, use_real) + class(integrated_solver), intent(inout) :: this + logical, intent(in) :: use_real + this%use_real_data = use_real + + if (this%config%verbose) then + if (use_real) then + print *, "[INTEGRATED SOLVER] Data mode set to: Real" + else + print *, "[INTEGRATED SOLVER] Data mode set to: Simple" + end if + end if + end subroutine solver_enable_real_data + + ! ==================== 私有方法 ==================== + + subroutine apply_initial_condition(this) + class(integrated_solver), intent(inout) :: this + + integer :: i, idx + real(wp) :: x + + if (this%config%verbose) then + print *, "[INTEGRATED SOLVER] Applying initial condition: ", & + trim(this%config%ic_type) + end if + + ! 简化的初始条件应用 + do i = this%domain%ist, this%domain%ied - 1 + idx = i - this%domain%ist + 1 + x = this%mesh%xcc(idx) + + select case (trim(this%config%ic_type)) + case ("step") + ! 阶跃函数 + if (x >= 0.5_wp .and. x <= 1.0_wp) then + this%solution%u(i) = 2.0_wp + else + this%solution%u(i) = 1.0_wp + end if + + case ("sin", "sine") + ! 正弦波 + this%solution%u(i) = sin(2.0_wp * 3.141592653589793_wp * x / & + this%config%domain_length) + + case ("gaussian") + ! 高斯脉冲 + this%solution%u(i) = exp(-((x - 0.5_wp) / 0.1_wp)**2) + + case default + ! 默认阶跃函数 + if (x >= 0.5_wp .and. x <= 1.0_wp) then + this%solution%u(i) = 2.0_wp + else + this%solution%u(i) = 1.0_wp + end if + end select + end do + + ! 同步旧场 + this%solution%un = this%solution%u + + if (this%config%verbose) then + print *, "[INTEGRATED SOLVER] Initial condition applied" + if (allocated(this%solution%u)) then + print *, " Min value: ", minval(this%solution%u) + print *, " Max value: ", maxval(this%solution%u) + end if + end if + end subroutine apply_initial_condition + + subroutine apply_boundary_conditions(this) + class(integrated_solver), intent(inout) :: this + + if (.not. allocated(this%bc)) then + if (this%config%verbose) then + print *, "[WARNING] No boundary condition allocated" + end if + return + end if + + select type(bc => this%bc) + type is (periodic_boundary) + ! 应用周期性边界条件 + call bc%apply(this%solution%u, & + this%domain%nghosts, & + this%domain%ist, & + this%domain%ied - 1) + + class default + ! 对于其他边界条件类型 + if (this%config%verbose) then + print *, "[WARNING] Boundary condition type not fully implemented" + end if + end select + end subroutine apply_boundary_conditions + + subroutine simple_time_step(this, dt) + class(integrated_solver), intent(inout) :: this + real(wp), intent(in) :: dt + + integer :: i + real(wp) :: dx, cfl + + ! 简化的时间步进(一阶迎风格式) + dx = this%mesh%dx + cfl = this%config%wave_speed * dt / dx + + if (cfl > 1.0_wp .and. this%config%verbose) then + print *, "[WARNING] CFL = ", cfl, " > 1.0" + end if + + ! 保存旧解 + this%solution%un = this%solution%u + + ! 一阶迎风格式(只更新内部点) + if (this%domain%ist + 1 <= this%domain%ied - 2) then + do i = this%domain%ist + 1, this%domain%ied - 2 + this%solution%u(i) = this%solution%un(i) - & + cfl * (this%solution%un(i) - this%solution%un(i-1)) + end do + end if + + ! 调试输出 + if (mod(this%current_step, 100) == 0 .and. this%config%verbose) then + print *, " [TIME STEP] Step: ", this%current_step, & + ", t = ", this%current_time, & + ", CFL = ", cfl, & + ", dt = ", dt + end if + end subroutine simple_time_step + + subroutine calculate_dt(this) + class(integrated_solver), intent(inout) :: this + + real(wp) :: cfl, dx + + dx = this%mesh%dx + + if (this%use_real_data) then + ! 真实计算使用CFL条件 + cfl = 0.8_wp ! CFL数 + this%config%dt = cfl * dx / abs(this%config%wave_speed) + else + ! 简单数据使用固定时间步长 + this%config%dt = 0.0025_wp + end if + + if (this%config%verbose .and. this%current_step == 0) then + print *, "[INTEGRATED SOLVER] Calculated dt = ", this%config%dt + end if + end subroutine calculate_dt + +end module solver_integrated_module \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/tests/CMakeLists.txt b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/CMakeLists.txt new file mode 100644 index 00000000..ced7163e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/CMakeLists.txt @@ -0,0 +1,137 @@ +# tests/CMakeLists.txt +message(STATUS "Configuring tests...") + +# 基础设施测试 +add_executable(test_infrastructure test_infrastructure.f90) +target_link_libraries(test_infrastructure + PRIVATE + infrastructure + core +) + +# 注册系统测试 +add_executable(test_registry test_registry.f90) +target_link_libraries(test_registry + PRIVATE + core + infrastructure +) + +# 物理模块测试 +add_executable(test_physics test_physics.f90) +target_link_libraries(test_physics + PRIVATE + physics + base +) + +# 组件管理器测试 +add_executable(test_component_manager test_component_manager.f90) +target_link_libraries(test_component_manager + PRIVATE + manager + infrastructure +) + +# 配置物理测试 +add_executable(test_config_physics test_config_physics.f90) +target_link_libraries(test_config_physics + PRIVATE + infrastructure + core +) + +# 求解器基础测试 +add_executable(test_solver_base test_solver_base.f90) +target_link_libraries(test_solver_base + PRIVATE + solver + infrastructure + core +) + +# 物理求解器测试 +add_executable(test_physics_solver test_physics_solver.f90) +target_link_libraries(test_physics_solver + PRIVATE + solver + infrastructure + core + physics + manager +) + +# 新增:简单物理求解器测试 +add_executable(test_physics_solver_simple test_physics_solver_simple.f90) +target_link_libraries(test_physics_solver_simple + PRIVATE + solver + infrastructure + core + physics + manager +) + +add_executable(test_simple_link test_simple_link.f90) +target_link_libraries(test_simple_link + PRIVATE + reconstructor + flux +) + + +add_executable(test_factory_simple test_factory_simple.f90) +target_link_libraries(test_factory_simple + PRIVATE + core + infrastructure + reconstructor + flux +) + +add_executable(test_domain_solution test_domain_solution.f90) +target_link_libraries(test_domain_solution + PRIVATE + infrastructure + core +) + +add_executable(test_component_manager_physics test_component_manager_physics.f90) +target_link_libraries(test_component_manager_physics + PRIVATE + manager + infrastructure + physics + core +) + +# 初始条件测试 +add_executable(test_initial_condition test_initial_condition.f90) +target_link_libraries(test_initial_condition + PRIVATE + initial_condition + infrastructure + core + base +) + +# 新增:简单求解器测试(只包含现有的) +# 注释掉缺失文件的测试,或者创建简单的占位符文件 +# add_executable(test_solver_integrated test_solver_integrated.f90) +# target_link_libraries(test_solver_integrated +# PRIVATE +# solver +# infrastructure +# core +# physics +# manager +# ) + +# 注释掉缺失的残差测试 +# add_executable(test_residual test_residual.f90) +# target_link_libraries(test_residual +# PRIVATE +# solver +# infrastructure +# physics +# ) diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_cfd_architecture.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_cfd_architecture.f90 new file mode 100644 index 00000000..1c40d467 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_cfd_architecture.f90 @@ -0,0 +1,117 @@ +! tests/test_architecture.f90 +program test_architecture + use, intrinsic :: iso_fortran_env, only: real64 + use registry_module + use config_module + use mesh_module + use component_manager_module + + implicit none + + print *, "=== CFD架构测试 ===" + print *, "" + + ! 测试1: 基本系统 + call test_basic_systems() + + ! 测试2: 组件注册 + call test_registry_integration() + + ! 测试3: 配置验证 + call test_config_validation() + + ! 测试4: 完整流程 + call test_full_workflow() + + print *, "" + print *, "=== 架构测试完成 ===" + +contains + + subroutine test_basic_systems() + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "1. 测试基本系统..." + print *, "-------------------" + + ! 测试配置 + call config_print(config) + print *, "✓ 配置创建成功" + + ! 测试网格 + call mesh%init(xmin=0.0_real64, xmax=1.0_real64, ncells=10) + call mesh%print_info() + print *, "✓ 网格创建成功" + print *, "" + end subroutine test_basic_systems + + subroutine test_registry_integration() + print *, "2. 测试注册系统集成..." + print *, "------------------------" + + call initialize_registry(verbose=.true.) + + ! 注册核心组件 + print *, "注册核心组件:" + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("boundary", "neumann") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + ! 显示注册内容 + call component_registry%list_simple() + print *, "注册表大小: ", registry_get_size() + + call cleanup_registry() + print *, "✓ 注册系统测试完成" + print *, "" + end subroutine test_registry_integration + + subroutine test_config_validation() + type(cfd_config) :: config + logical :: is_valid + + print *, "3. 测试配置验证..." + print *, "-------------------" + + ! 测试有效配置 + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_real64 + + print *, "测试配置:" + print *, " - 重构器: ", trim(config%recon_scheme), " 阶数: ", config%spatial_order + print *, " - 通量: ", trim(config%flux_type) + print *, " - 波速: ", config%wave_speed + + ! 这里调用验证函数(需要先实现) + ! is_valid = validate_config(config) + print *, "✓ 配置验证接口准备就绪" + print *, "" + end subroutine test_config_validation + + subroutine test_full_workflow() + print *, "4. 测试完整流程..." + print *, "-------------------" + + print *, "步骤1: 初始化系统 ✓" + print *, "步骤2: 创建网格 ✓" + print *, "步骤3: 设置配置 ✓" + print *, "步骤4: 创建组件 (待实现)" + print *, "步骤5: 初始化解 (待实现)" + print *, "步骤6: 时间推进 (待实现)" + print *, "步骤7: 输出结果 (待实现)" + print *, "" + + print *, "✓ 流程框架定义完成" + end subroutine test_full_workflow + +end program test_architecture \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_component_manager.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_component_manager.f90 new file mode 100644 index 00000000..cf4cf38a --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_component_manager.f90 @@ -0,0 +1,56 @@ +! tests/test_component_manager.f90 (修正版) +program test_component_manager + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + implicit none + + type(cfd_config) :: config + + print *, "=== Component Manager Test (简化版) ===" + print *, "" + + ! 测试1: 基本配置 + print *, "1. Testing basic configuration..." + print *, "-----------------------------------" + + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.5_wp + + call config_print(config) + print *, "" + + print *, "2. Testing component manager info (简化)..." + print *, "------------------------------------------" + print *, "Component manager functions (简化版本):" + print *, " - Configuration validation available" + print *, " - Component creation framework ready" + print *, "" + + print *, "3. Testing WENO3 configuration..." + print *, "---------------------------------" + + call config_with_reconstruction(config, "weno3", 3) + + print *, "WENO3 configuration:" + print *, " Scheme: ", trim(config%recon_scheme) + print *, " Order: ", config%spatial_order + print *, "" + + ! 测试4: 错误配置测试 + print *, "4. Testing error handling..." + print *, "-----------------------------------" + + config%recon_scheme = "unknown_scheme" + config%flux_type = "unknown_flux" + + print *, "Invalid configuration test:" + print *, " Scheme: ", trim(config%recon_scheme) + print *, " Flux: ", trim(config%flux_type) + print *, "" + + print *, "=== Component manager test completed (简化版) ===" + print *, "下一步: 完善组件管理器功能" + +end program test_component_manager \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_component_manager_physics.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_component_manager_physics.f90 new file mode 100644 index 00000000..f2becca9 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_component_manager_physics.f90 @@ -0,0 +1,120 @@ +! tests/test_component_manager_physics.f90 (简化版) +program test_component_manager_physics + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + use component_manager_module, only: component_manager_info, validate_config + + implicit none + + type(cfd_config) :: config + logical :: is_valid + + print *, "=== Component Manager Physics Test (Simplified) ===" + print *, "" + + ! 测试1: 显示组件管理器信息 + print *, "1. Testing component manager info..." + print *, "-------------------------------------" + call component_manager_info() + print *, "" + + ! 测试2: 物理模块测试(默认) + print *, "2. Testing physics module with default configuration..." + print *, "------------------------------------------------------" + + config%verbose = .true. + call config_print(config) + + ! 验证配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Default configuration is valid" + else + print *, "[ERROR] Default configuration is invalid" + end if + print *, "" + + ! 测试3: 测试物理配置 + print *, "3. Testing physics configuration..." + print *, "------------------------------------" + + ! 修改物理参数 + config%equation_type = "linear_advection" + config%problem_type = "linear_advection" + config%wave_speed = 2.5_wp + config%domain_length = 3.0_wp + + print *, "Modified physics configuration:" + print *, " Equation type: ", trim(config%equation_type) + print *, " Problem type: ", trim(config%problem_type) + print *, " Wave speed: ", config%wave_speed + print *, " Domain length: ", config%domain_length + print *, " Physics enabled: ", config%enable_physics + + ! 验证修改后的配置 + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Modified physics configuration is valid" + else + print *, "[ERROR] Modified physics configuration is invalid" + end if + print *, "" + + ! 测试4: 数值组件测试 + print *, "4. Testing numerical components with physics..." + print *, "-----------------------------------------------" + + call config_with_reconstruction(config, "weno3", 3) + config%flux_type = "rusanov" + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Combined physics+numerics configuration is valid" + else + print *, "[ERROR] Combined configuration is invalid" + end if + print *, "" + + ! 测试5: 物理模块禁用测试 + print *, "5. Testing physics module disabled..." + print *, "---------------------------------------" + + config%enable_physics = .false. + config%verbose = .false. + + is_valid = validate_config(config) + if (is_valid) then + print *, "[OK] Configuration valid even with physics disabled" + else + print *, "[ERROR] Configuration should be valid with physics disabled" + end if + print *, "" + + ! 测试6: 错误配置测试 + print *, "6. Testing error handling..." + print *, "-----------------------------" + + config%verbose = .true. + config%enable_physics = .true. + config%recon_scheme = "unknown_scheme" + config%flux_type = "unknown_flux" + config%equation_type = "unknown_equation" + config%problem_type = "unknown_problem" + + is_valid = validate_config(config) + if (.not. is_valid) then + print *, "[OK] Invalid configuration correctly rejected" + else + print *, "[ERROR] Invalid configuration should have been rejected" + end if + print *, "" + + print *, "=== Component Manager Physics Test Summary ===" + print *, "✓ Component manager info works" + print *, "✓ Configuration validation works with physics" + print *, "✓ Error handling works correctly" + print *, "✓ Combined physics+numerics validation works" + print *, "" + print *, "下一步: 集成物理模块到求解器框架" + +end program test_component_manager_physics \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_config_physics.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_config_physics.f90 new file mode 100644 index 00000000..c6fef5c0 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_config_physics.f90 @@ -0,0 +1,141 @@ +! tests/test_config_physics.f90 (修复版) +program test_config_physics + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + + implicit none + + type(cfd_config) :: config + + print *, "=== Configuration Physics Test (Simplified) ===" + print *, "" + + ! 测试1: 默认配置 + print *, "1. Testing default configuration..." + print *, "-----------------------------------" + call config_print(config) + print *, "" + + ! 测试2: 验证基础物理字段 + print *, "2. Testing basic physics fields..." + print *, "----------------------------------" + + print *, "Verifying default physics fields:" + + if (trim(config%equation_type) == "linear_advection") then + print *, " ✓ Default equation type: linear_advection" + else + print *, " ✗ Unexpected equation type: ", trim(config%equation_type) + end if + + if (trim(config%problem_type) == "linear_advection") then + print *, " ✓ Default problem type: linear_advection" + else + print *, " ✗ Unexpected problem type: ", trim(config%problem_type) + end if + + if (abs(config%domain_length - 2.0_wp) < 1e-10_wp) then + print *, " ✓ Default domain length: 2.0" + else + print *, " ✗ Unexpected domain length: ", config%domain_length + end if + + if (config%enable_physics) then + print *, " ✓ Physics enabled by default" + else + print *, " ✗ Physics not enabled by default" + end if + + print *, "" + + ! 测试3: 使用类型绑定的方法(正确的方法名) + print *, "3. Testing type-bound procedures..." + print *, "--------------------------------------" + + call config%set_physics_parameters( & + equation_type="burgers_equation", & + problem_type="sod_shock_tube", & + domain_length=3.0_wp, & + enable_physics=.false.) + + print *, "After set_physics_parameters:" + print *, " Equation type: ", trim(config%equation_type) + print *, " Problem type: ", trim(config%problem_type) + print *, " Domain length: ", config%domain_length + print *, " Physics enabled: ", config%enable_physics + + if (trim(config%equation_type) == "burgers_equation") then + print *, " ✓ Equation type modified successfully via set_physics_parameters" + end if + + if (trim(config%problem_type) == "sod_shock_tube") then + print *, " ✓ Problem type modified successfully via set_physics_parameters" + end if + + if (abs(config%domain_length - 3.0_wp) < 1e-10_wp) then + print *, " ✓ Domain length modified successfully via set_physics_parameters" + end if + + if (.not. config%enable_physics) then + print *, " ✓ Physics disabled successfully via set_physics_parameters" + end if + + print *, "" + + ! 测试4: 调用get_physics_info方法 + print *, "4. Testing get_physics_info method..." + print *, "--------------------------------------" + call config%get_physics_info() + print *, "" + + ! 测试5: 高斯脉冲配置 + print *, "5. Testing Gaussian pulse configuration..." + print *, "-----------------------------------------" + + config%ic_type = "gaussian" + config%pulse_center = 0.6_wp + config%pulse_width = 0.15_wp + + print *, "Gaussian pulse parameters:" + print *, " IC type: ", trim(config%ic_type) + print *, " Center: ", config%pulse_center + print *, " Width: ", config%pulse_width + + if (trim(config%ic_type) == "gaussian") then + print *, " ✓ Gaussian IC type set" + end if + + if (abs(config%pulse_center - 0.6_wp) < 1e-10_wp) then + print *, " ✓ Pulse center set" + end if + + if (abs(config%pulse_width - 0.15_wp) < 1e-10_wp) then + print *, " ✓ Pulse width set" + end if + + print *, "" + + ! 测试6: 重构配置 + print *, "6. Testing reconstruction configuration..." + print *, "------------------------------------------" + + call config_with_reconstruction(config, "weno", 5) + + print *, "Reconstruction configuration:" + print *, " Scheme: ", trim(config%recon_scheme) + print *, " Order: ", config%spatial_order + + if (trim(config%recon_scheme) == "weno" .and. config%spatial_order == 5) then + print *, " ✓ WENO5 configuration successful" + else + print *, " ✗ Reconstruction configuration failed" + end if + + print *, "" + + print *, "=== Configuration Physics Test Complete ===" + print *, "✓ Config module updated with physics support" + print *, "✓ Fields can be directly accessed and modified" + print *, "✓ Type-bound procedures work correctly" + +end program test_config_physics \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_domain_solution.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_domain_solution.f90 new file mode 100644 index 00000000..ff659bac --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_domain_solution.f90 @@ -0,0 +1,102 @@ +! tests/test_domain_solution.f90 +program test_domain_solution + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create, solution_reset + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + real(wp), allocatable :: initial_values(:) + integer :: i + + print *, "=== Domain and Solution Test ===" + print *, "" + + ! 测试1: 不同重构方案的ghost层计算 + print *, "1. Testing ghost layer calculation..." + print *, "--------------------------------------" + + ! ENO3 + call config_with_reconstruction(config, "eno", 3) + config%verbose = .false. + call mesh%init(ncells=10) + domain = domain_create(config, mesh) + print *, "ENO3: nghosts = ", domain%nghosts, " (expected: 3)" + + ! WENO3 + call config_with_reconstruction(config, "weno3", 3) + domain = domain_create(config, mesh) + print *, "WENO3: nghosts = ", domain%nghosts, " (expected: 2)" + + ! WENO5 + call config_with_reconstruction(config, "weno", 5) + domain = domain_create(config, mesh) + print *, "WENO5: nghosts = ", domain%nghosts, " (expected: 3)" + print *, "" + + ! 测试2: Solution数组 + print *, "2. Testing solution arrays..." + print *, "------------------------------" + + call config_with_reconstruction(config, "eno", 3) + config%verbose = .true. + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + domain = domain_create(config, mesh) + call domain%print_info() + print *, "" + + solution = solution_create(domain) + call solution%print_info() + print *, "" + + ! 测试3: 初始化和更新 + print *, "3. Testing initialization and update..." + print *, "----------------------------------------" + + allocate(initial_values(mesh%ncells)) + do i = 1, mesh%ncells + initial_values(i) = sin(2.0_wp * 3.14159265358979_wp * mesh%xcc(i) / mesh%L) + end do + + call solution%initialize(initial_values) + print *, "After initialization:" + print *, " u range: ", minval(solution%u), " to ", maxval(solution%u) + print *, " un range: ", minval(solution%un), " to ", maxval(solution%un) + + ! 修改当前解,测试更新 + solution%u = solution%u * 2.0_wp + call solution%update_old_field() + print *, "After update: max|u - un| = ", maxval(abs(solution%u - solution%un)) + print *, "" + + ! 测试4: 重置 + print *, "4. Testing reset..." + print *, "-------------------" + + call solution_reset(solution) + print *, "After reset:" + print *, " u max: ", maxval(abs(solution%u)) + print *, " un max: ", maxval(abs(solution%un)) + print *, " flux max: ", maxval(abs(solution%flux)) + print *, "" + + deallocate(initial_values) + + print *, "=== Test Summary ===" + print *, "✓ Ghost layer calculation works" + print *, "✓ Domain creation works" + print *, "✓ Solution arrays work" + print *, "✓ Initialization works" + print *, "✓ Field update works" + print *, "✓ Reset works" + print *, "" + print *, "Ready for next step: Implementing Physics modules" + +end program test_domain_solution \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_factory_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_factory_simple.f90 new file mode 100644 index 00000000..db65da7c --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_factory_simple.f90 @@ -0,0 +1,58 @@ +! tests/test_factory_simple.f90 (修复版) +program test_factory_simple + use base_modules, only: wp ! ← 添加这行 + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use reconstructor_base_module, only: reconstructor_base + use eno_reconstructor_module, only: eno_reconstructor + use weno3_reconstructor_module, only: weno3_reconstructor + use flux_base_module, only: flux_calculator_base + use rusanov_flux_module, only: rusanov_flux + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(eno_reconstructor) :: eno + type(weno3_reconstructor) :: weno3 + type(rusanov_flux) :: rusanov + + print *, "=== Factory Pattern Simple Test ===" + print *, "" + + ! Test 1: Basic systems + print *, "1. Testing basic systems..." + print *, "-----------------------------" + call config_print(config) + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 2: Creating reconstructors + print *, "2. Testing reconstructors..." + print *, "------------------------------" + + ! 创建并测试ENO重构器 + print *, "Creating ENO reconstructor..." + eno = eno_reconstructor() ! 使用构造函数 + call eno%info() ! 必须调用info方法 + + print *, "" + print *, "Creating WENO3 reconstructor..." + weno3 = weno3_reconstructor() ! 使用构造函数 + call weno3%info() ! 必须调用info方法 + print *, "" + + ! Test 3: Creating flux calculator + print *, "3. Testing flux calculator..." + print *, "-------------------------------" + + print *, "Creating Rusanov flux calculator..." + rusanov = rusanov_flux() ! 使用构造函数 + call rusanov%info() ! 必须调用info方法 + print *, "" + + print *, "=== Factory pattern simple test completed successfully ===" + +end program test_factory_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_infrastructure.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_infrastructure.f90 new file mode 100644 index 00000000..22fa92d1 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_infrastructure.f90 @@ -0,0 +1,56 @@ +! tests/test_infrastructure.f90 (原test_basic_only.f90) +program test_infrastructure + use base_modules, only: wp + use config_module, only: cfd_config, config_print + use mesh_module, only: mesh_type + use registry_module, only: registry_init, registry_cleanup, & + register_component_simple, list_components + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + + print *, "=== 基础设施测试 ===" + print *, "" + + ! 测试1: 配置 + print *, "1. 测试配置模块..." + print *, "-------------------" + call config_print(config) + print *, "" + + ! 测试2: 网格 + print *, "2. 测试网格模块..." + print *, "------------------" + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=5) + print *, "网格初始化:" + print *, " 单元数: ", mesh%ncells + print *, " 节点数: ", mesh%nnodes + print *, " 网格间距: ", mesh%dx + print *, "" + + ! 测试3: 注册系统 + print *, "3. 测试注册系统..." + print *, "------------------" + + call registry_init() + + ! 注册组件(使用简化版本) + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("flux", "rusanov") + + ! 列出组件 + call list_components() + print *, "" + + ! 清理 + call registry_cleanup() + + print *, "=== 基础设施测试通过 ===" + print *, "✓ 配置模块工作正常" + print *, "✓ 网格模块工作正常" + print *, "✓ 注册系统工作正常" + +end program test_infrastructure \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_initial_condition.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_initial_condition.f90 new file mode 100644 index 00000000..1c6064b6 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_initial_condition.f90 @@ -0,0 +1,96 @@ +! tests/test_initial_condition.f90 +program test_initial_condition + use base_modules, only: wp + use config_module, only: cfd_config + use mesh_module, only: mesh_type + use domain_module, only: domain_type, domain_create + use solution_module, only: solution_type, solution_create + use ic_factory_module, only: create_initial_condition + use ic_base_module, only: initial_condition + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(domain_type) :: domain + type(solution_type) :: solution + class(initial_condition), allocatable :: ic + integer :: i + + print *, "=== 初始条件模块测试 ===" + print *, "" + + ! 创建配置和网格 + config%verbose = .false. + config%recon_scheme = "eno" + config%spatial_order = 3 + + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + domain = domain_create(config, mesh) + solution = solution_create(domain) + + ! 测试1: 阶跃函数初始条件 + print *, "1. 测试阶跃函数初始条件..." + call create_initial_condition("step", ic) + + if (allocated(ic)) then + call ic%apply(solution) + print *, " 成功应用阶跃函数初始条件" + print *, " 解范围: ", minval(solution%u), " 到 ", maxval(solution%u) + + ! 检查结果 + if (abs(maxval(solution%u) - 2.0_wp) < 1e-10_wp .and. & + abs(minval(solution%u) - 1.0_wp) < 1e-10_wp) then + print *, " ✓ 阶跃函数测试通过" + else + print *, " ✗ 阶跃函数测试失败" + end if + end if + + deallocate(ic) + print *, "" + + ! 测试2: 正弦波初始条件 + print *, "2. 测试正弦波初始条件..." + call create_initial_condition("sin", ic) + + if (allocated(ic)) then + call solution%reset() + call ic%apply(solution) + print *, " 成功应用正弦波初始条件" + print *, " 解范围: ", minval(solution%u), " 到 ", maxval(solution%u) + print *, " ✓ 正弦波测试通过" + end if + + deallocate(ic) + print *, "" + + ! 测试3: 高斯脉冲初始条件 + print *, "3. 测试高斯脉冲初始条件..." + call create_initial_condition("gaussian", ic) + + if (allocated(ic)) then + call solution%reset() + call ic%apply(solution) + print *, " 成功应用高斯脉冲初始条件" + print *, " 解范围: ", minval(solution%u), " 到 ", maxval(solution%u) + print *, " ✓ 高斯脉冲测试通过" + end if + + deallocate(ic) + print *, "" + + ! 测试4: 错误处理 + print *, "4. 测试错误处理..." + call create_initial_condition("unknown", ic) + + if (allocated(ic)) then + print *, " ✓ 错误处理测试通过(使用了默认初始条件)" + else + print *, " ✗ 错误处理测试失败" + end if + + print *, "" + print *, "=== 初始条件模块测试完成 ===" + +end program test_initial_condition \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_physics.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_physics.f90 new file mode 100644 index 00000000..784a3ffc --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_physics.f90 @@ -0,0 +1,20 @@ +! tests/test_physics.f90 +program test_physics + use base_modules, only: wp, ip + implicit none + + print *, "=== 物理模块测试(简化版)===" + print *, "" + + print *, "1. 测试基本物理概念..." + print *, " ✓ 物理模块占位符" + print *, "" + + print *, "2. 测试阶跃函数逻辑..." + print *, " ✓ 阶跃函数逻辑占位符" + print *, "" + + print *, "=== 物理模块测试完成 ===" + print *, "注意: 这是简化测试,物理模块尚未完全集成" + +end program test_physics \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_physics_solver.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_physics_solver.f90 new file mode 100644 index 00000000..0c83641f --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_physics_solver.f90 @@ -0,0 +1,85 @@ +! tests/test_physics_solver.f90 (简化修正版) +program test_physics_solver + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction + use mesh_module, only: mesh_type + use physics_solver_module, only: physics_solver, SOLVER_COMPLETED + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(physics_solver) :: psolver + real(wp) :: final_time + integer :: state + + print *, "=== Physics Solver Test (简化版) ===" + print *, "" + + ! 测试1: 创建物理求解器 + print *, "1. Creating physics solver..." + print *, "-----------------------------" + + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%dt = 0.01_wp + config%enable_physics = .true. + + print *, "Configuration:" + print *, " Scheme: ", trim(config%recon_scheme) + print *, " dt: ", config%dt + print *, " Physics enabled: ", config%enable_physics + + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + ! 设置求解器配置和网格 + psolver%config = config + psolver%mesh = mesh + + print *, " Solver created successfully" + print *, "" + + ! 测试2: 初始化 + print *, "2. Initializing physics solver..." + print *, "---------------------------------" + + call psolver%initialize() + state = psolver%get_state() + + print *, " State after initialization: ", state + print *, " Expected: initialized (1)" + print *, "" + + ! 测试3: 运行一小段时间 + print *, "3. Running physics solver (short time)..." + print *, "------------------------------------------" + + call psolver%run_to_time(0.02_wp) + state = psolver%get_state() + + print *, " State after run: ", state + print *, " Expected: completed (3)" + print *, " Current time: ", psolver%current_time + print *, " Current step: ", psolver%current_step + print *, "" + + ! 测试4: 清理 + print *, "4. Testing cleanup..." + print *, "----------------------" + + call psolver%cleanup() + state = psolver%get_state() + + print *, " State after cleanup: ", state + print *, " Expected: uninitialized (0)" + print *, "" + + ! 结果验证 + print *, "=== Test Summary ===" + if (state == 0) then + print *, "✓ All basic tests passed" + else + print *, "✗ Some tests failed" + end if + +end program test_physics_solver \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_physics_solver_simple.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_physics_solver_simple.f90 new file mode 100644 index 00000000..13312efd --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_physics_solver_simple.f90 @@ -0,0 +1,161 @@ +! tests/test_physics_solver_simple.f90 +program test_physics_solver_simple + use base_modules, only: wp + use config_module, only: cfd_config, config_with_reconstruction + use mesh_module, only: mesh_type + use physics_solver_module, only: physics_solver, SOLVER_COMPLETED + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(physics_solver) :: solver + real(wp) :: final_time, final_step + integer :: state + + print *, "=========================================" + print *, " 简单物理求解器测试" + print *, "=========================================" + print *, "" + + ! 步骤1: 配置 + print *, "[步骤1] 配置求解器..." + print *, "---------------------" + + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%dt = 0.01_wp + config%final_time = 0.1_wp + config%wave_speed = 1.0_wp + config%ic_type = "step" + config%boundary_type = "periodic" + config%equation_type = "linear_advection" + config%problem_type = "linear_advection" + config%enable_physics = .true. + config%domain_length = 1.0_wp + + print *, "配置参数:" + print *, " 重构格式: ", trim(config%recon_scheme) + print *, " 时间步长: ", config%dt + print *, " 最终时间: ", config%final_time + print *, " 波速: ", config%wave_speed + print *, "" + + ! 步骤2: 创建网格 + print *, "[步骤2] 创建网格..." + print *, "-------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + + print *, "网格信息:" + print *, " 单元数: ", mesh%ncells + print *, " 节点数: ", mesh%nnodes + print *, " 网格间距: ", mesh%dx + print *, "" + + ! 步骤3: 创建求解器 + print *, "[步骤3] 创建求解器..." + print *, "---------------------" + + solver = physics_solver(config, mesh) + + print *, "求解器创建成功" + print *, " 初始状态: ", solver%get_state() + print *, "" + + ! 步骤4: 初始化 + print *, "[步骤4] 初始化求解器..." + print *, "-----------------------" + + call solver%initialize() + + state = solver%get_state() + print *, "初始化完成" + print *, " 状态: ", state + print *, " 当前时间: ", solver%current_time + print *, " 当前步数: ", solver%current_step + print *, "" + + ! 步骤5: 运行求解器 + print *, "[步骤5] 运行求解器..." + print *, "---------------------" + + call solver%run_to_time(config%final_time) + + state = solver%get_state() + print *, "运行完成" + print *, " 状态: ", state + print *, " 最终时间: ", solver%current_time + print *, " 总步数: ", solver%current_step + print *, "" + + ! 步骤6: 保存结果 + print *, "[步骤6] 保存结果..." + print *, "-------------------" + + final_time = solver%current_time + final_step = real(solver%current_step, wp) + state = solver%get_state() + + print *, "保存的结果:" + print *, " 状态: ", state + print *, " 时间: ", final_time + print *, " 步数: ", final_step + print *, "" + + ! 步骤7: 清理求解器 + print *, "[步骤7] 清理求解器..." + print *, "---------------------" + + call solver%cleanup() + + print *, "清理后状态:" + print *, " 状态: ", solver%get_state() + print *, " 时间: ", solver%current_time + print *, " 步数: ", solver%current_step + print *, "" + + ! 步骤8: 验证结果 + print *, "[步骤8] 验证结果..." + print *, "-------------------" + + print *, "验证标准:" + print *, " 1. 运行后状态应为 COMPLETED (", SOLVER_COMPLETED, ")" + print *, " 2. 最终时间应接近 ", config%final_time + print *, " 3. 步数应大于 0" + print *, "" + + if (state == SOLVER_COMPLETED) then + print *, "✓ 状态验证通过: COMPLETED" + else + print *, "✗ 状态验证失败: 期望 ", SOLVER_COMPLETED, ", 实际 ", state + end if + + if (abs(final_time - config%final_time) < 1e-5_wp) then + print *, "✓ 时间验证通过: ", final_time, " ≈ ", config%final_time + else + print *, "✗ 时间验证失败: ", final_time, " ≠ ", config%final_time + end if + + if (final_step > 0) then + print *, "✓ 步数验证通过: ", final_step, " > 0" + else + print *, "✗ 步数验证失败: ", final_step, " ≤ 0" + end if + + print *, "" + + ! 最终判断 + if (state == SOLVER_COMPLETED .and. & + abs(final_time - config%final_time) < 1e-5_wp .and. & + final_step > 0) then + print *, "=========================================" + print *, " 所有测试通过! ✓" + print *, "=========================================" + else + print *, "=========================================" + print *, " 测试失败 ✗" + print *, "=========================================" + end if + +end program test_physics_solver_simple \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_registry.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_registry.f90 new file mode 100644 index 00000000..e82651ff --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_registry.f90 @@ -0,0 +1,87 @@ +! tests/test_registry.f90 (原test_minimal_simple.f90) +program test_registry + use base_modules, only: wp + use registry_module + use config_module + use mesh_module + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== 注册系统功能测试 ===" + print *, "" + + ! 测试1: 配置系统 + print *, "1. 测试配置系统" + print *, "--------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! 测试2: 网格系统 + print *, "2. 测试网格系统" + print *, "--------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! 测试3: 注册系统 + print *, "3. 测试注册系统" + print *, "--------------" + + call registry_init() + + ! 注册组件 + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call list_components() + print *, "注册表大小: ", registry_get_size() + print *, "" + + ! 测试组件查找 + print *, "4. 测试组件查找" + print *, "--------------" + + if (has_component_simple("reconstructor", "eno")) then + print *, "找到: reconstructor.eno" + else + print *, "未找到: reconstructor.eno" + end if + + if (has_component_simple("reconstructor", "unknown")) then + print *, "找到: reconstructor.unknown" + else + print *, "未找到: reconstructor.unknown" + end if + print *, "" + + ! 测试获取可用组件 + print *, "5. 测试注册系统功能" + print *, "------------------" + print *, "注册表已初始化: ", registry_is_initialized() + print *, "组件数量: ", registry_get_size() + print *, "" + + ! 清理 + call registry_cleanup() + + print *, "=== 注册系统测试完成 ===" + +end program test_registry \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_simple_link.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_simple_link.f90 new file mode 100644 index 00000000..71cc614e --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_simple_link.f90 @@ -0,0 +1,78 @@ +! tests/test_simple_link.f90 +program test_simple_link + use base_modules, only: wp ! ← 添加这行 + use registry_module + use config_module + use mesh_module + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + integer :: i + + print *, "=== Minimal Functionality Test ===" + print *, "" + + ! Test 1: Configuration system + print *, "1. Testing configuration system" + print *, "--------------------------------" + + call config_print(config) + + call config_with_reconstruction(config, "eno", 3) + + call config_print(config) + print *, "" + + ! Test 2: Mesh system + print *, "2. Testing mesh system" + print *, "----------------------" + + call mesh%init(xmin=0.0_wp, xmax=1.0_wp, ncells=10) + call mesh%print_info() + print *, "" + + ! Test 3: Registry system + print *, "3. Testing registry system" + print *, "--------------------------" + + call registry_init() + + ! Register some components + call register_component_simple("reconstructor", "eno") + call register_component_simple("reconstructor", "weno3") + call register_component_simple("reconstructor", "weno5") + call register_component_simple("flux", "rusanov") + call register_component_simple("flux", "engquist-osher") + call register_component_simple("boundary", "periodic") + call register_component_simple("boundary", "dirichlet") + call register_component_simple("integrator", "rk1") + call register_component_simple("integrator", "rk2") + call register_component_simple("integrator", "rk3") + + call list_components() + print *, "Registry size: ", registry_get_size() + print *, "" + + ! Test component lookup + print *, "4. Testing component lookup" + print *, "---------------------------" + if (has_component_simple("reconstructor", "eno")) then + print *, "Found: reconstructor.eno" + else + print *, "Not found: reconstructor.eno" + end if + + if (has_component_simple("reconstructor", "unknown")) then + print *, "Found: reconstructor.unknown" + else + print *, "Not found: reconstructor.unknown" + end if + print *, "" + + ! Cleanup + call registry_cleanup() + + print *, "=== Minimal test completed successfully ===" + +end program test_simple_link \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_solver_base.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_solver_base.f90 new file mode 100644 index 00000000..6cfe47e4 --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_solver_base.f90 @@ -0,0 +1,99 @@ +! tests/test_solver_base.f90 (修复版) +program test_solver_base + ! 所有 USE 语句必须在程序开始处 + use base_modules, only: wp + use config_module, only: cfd_config, config_print, config_with_reconstruction + use mesh_module, only: mesh_type + use solver_base_module, only: solver_base, SOLVER_UNINITIALIZED, & + SOLVER_INITIALIZED, SOLVER_COMPLETED, SOLVER_ERROR + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(solver_base) :: solver + integer :: state + + print *, "=== Solver Base Test ===" + print *, "" + + ! 测试1: 创建求解器 + print *, "1. Creating solver..." + print *, "----------------------" + + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%dt = 0.01_wp + + call config_print(config) + + call mesh%init(xmin=0.0_wp, xmax=2.0_wp, ncells=10) + + solver = solver_base(config, mesh) + call solver%print_info() + print *, "" + + ! 测试2: 初始化 + print *, "2. Initializing solver..." + print *, "-------------------------" + + call solver%initialize() + state = solver%get_state() + print *, "State after initialization: ", state + print *, "Expected: ", SOLVER_INITIALIZED + print *, "Match? ", state == SOLVER_INITIALIZED + print *, "Error message: '", trim(solver%get_error()), "'" + print *, "" + + ! 测试3: 运行求解器 + print *, "3. Running solver..." + print *, "--------------------" + + call solver%run_to_time(0.05_wp) + state = solver%get_state() + print *, "State after run: ", state + print *, "Expected: ", SOLVER_COMPLETED + print *, "Match? ", state == SOLVER_COMPLETED + print *, "Current time: ", solver%current_time + print *, "Current step: ", solver%current_step + print *, "" + + ! 测试4: 再次运行(从已完成状态) + print *, "4. Running again from completed state..." + print *, "----------------------------------------" + + ! 需要先清理才能重新运行 + call solver%cleanup() + call solver%initialize() + call solver%run_to_time(0.1_wp) + + call solver%print_info() + print *, "" + + ! 测试5: 错误处理 + print *, "5. Testing error states..." + print *, "--------------------------" + + ! 创建一个未初始化的求解器 + call solver%cleanup() + state = solver%get_state() + print *, "Uninitialized state: ", state + print *, "Expected: ", SOLVER_UNINITIALIZED + print *, "Match? ", state == SOLVER_UNINITIALIZED + + ! 尝试运行未初始化的求解器 + call solver%run_to_time(0.01_wp) + state = solver%get_state() + print *, "State after error: ", state + print *, "Expected: ", SOLVER_ERROR + print *, "Match? ", state == SOLVER_ERROR + print *, "Error message: '", trim(solver%get_error()), "'" + print *, "" + + print *, "=== Solver Base Test Complete ===" + print *, "✓ Solver base class works" + print *, "✓ State management works" + print *, "✓ Time stepping framework works" + print *, "✓ Error handling works" + +end program test_solver_base \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_solver_framework.f90 b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_solver_framework.f90 new file mode 100644 index 00000000..6754323d --- /dev/null +++ b/example/1d-linear-convection/weno3/fortran/registry/03g/tests/test_solver_framework.f90 @@ -0,0 +1,91 @@ +! tests/test_solver_framework.f90 +program test_solver_framework + use, intrinsic :: iso_fortran_env, only: real64 + use config_module, only: cfd_config, config_print, config_with_reconstruction + use mesh_module, only: mesh_type + use solver_module, only: cfd_solver, solver_create, solver_run, solver_cleanup + use solver_module, only: SOLVER_UNINITIALIZED, SOLVER_INITIALIZED, & + SOLVER_COMPLETED, SOLVER_ERROR + + implicit none + + type(cfd_config) :: config + type(mesh_type) :: mesh + type(cfd_solver) :: solver + + print *, "=== 求解器框架测试 ===" + print *, "" + + ! 测试1: 基本创建 + print *, "1. 测试求解器创建..." + print *, "----------------------" + + ! 创建配置 + config%verbose = .true. + call config_with_reconstruction(config, "eno", 3) + config%flux_type = "rusanov" + config%wave_speed = 1.0_real64 + config%dt = 0.01_real64 + + call config_print(config) + print *, "" + + ! 创建网格 + call mesh%init(xmin=0.0_real64, xmax=2.0_real64, ncells=20) + call mesh%print_info() + print *, "" + + ! 创建求解器 + solver = solver_create(config, mesh) + print *, "✓ 求解器创建成功" + print *, " 状态: ", solver%get_state() + print *, "" + + ! 测试2: 求解器初始化 + print *, "2. 测试求解器初始化..." + print *, "------------------------" + + call solver%initialize() + print *, "✓ 求解器初始化完成" + print *, " 状态: ", solver%get_state() + print *, " 错误信息: '", trim(solver%get_error()), "'" + print *, "" + + ! 测试3: 简单运行 + print *, "3. 测试求解器运行..." + print *, "----------------------" + + call solver_run(solver, 0.05_real64) ! 运行到0.05秒 + print *, "✓ 求解器运行完成" + print *, " 最终状态: ", solver%get_state() + print *, "" + + ! 测试4: 清理 + print *, "4. 测试求解器清理..." + print *, "----------------------" + + call solver_cleanup(solver) + print *, "✓ 求解器清理完成" + print *, " 状态: ", solver%get_state() + print *, "" + + ! 测试5: 错误处理 + print *, "5. 测试错误处理..." + print *, "-------------------" + + ! 尝试重复初始化 + call solver%initialize() + print *, " 重复初始化状态: ", solver%get_state() + print *, " 错误信息: '", trim(solver%get_error()), "'" + + call solver_cleanup(solver) + print *, "" + + print *, "=== 框架测试总结 ===" + print *, "✓ 求解器创建/初始化/运行/清理流程验证完成" + print *, "✓ 状态管理正常工作" + print *, "✓ 错误处理机制就绪" + print *, "" + print *, "下一步: 添加实际数值计算功能" + +end program test_solver_framework \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01/julia/boundary.jl b/example/1d-linear-convection/weno3/julia/01/julia/boundary.jl new file mode 100644 index 00000000..5b0baaf4 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01/julia/boundary.jl @@ -0,0 +1,90 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01/julia/test/test_boundary.jl b/example/1d-linear-convection/weno3/julia/01/julia/test/test_boundary.jl new file mode 100644 index 00000000..ef27d682 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01/julia/test/test_boundary.jl @@ -0,0 +1,57 @@ +# julia/test/test_boundary.jl +using NPZ +include("../boundary.jl") + +struct MockConfig + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + debug::Bool +end + +struct MockDomain + nghosts::Int + ist::Int # Julia 本地索引(1-based) + ied::Int + ntcells::Int +end + +struct MockCfd + config::MockConfig + domain::MockDomain +end + +# ===== 关键:使用 Julia 本地索引规则 ===== +nghosts = 2 +ncells = 40 +ist = nghosts + 1 # = 3 +ied = ist + ncells # = 43 +ntcells = ncells + 2 * nghosts # = 44 + +config = MockConfig("dirichlet", 0.5, 1.5, true) +domain = MockDomain(nghosts, ist, ied, ntcells) +cfd_mock = MockCfd(config, domain) + +# 加载 Python 生成的 u_input.npy +# 注意:Python u[0] → Julia u[1],所以内容完全对应 +u_input = npzread("../../python/u_input.npy") +@assert length(u_input) == ntcells "数组长度不匹配!" + +# 测试三种边界 +test_cases = [ + ("periodic", PeriodicBoundary(cfd_mock)), + ("dirichlet", DirichletBoundary(cfd_mock)), + ("neumann", NeumannBoundary(cfd_mock)) +] + +for (name, bc) in test_cases + u = copy(u_input) + apply!(bc, u) + + u_py = npzread("../../python/u_$(name)_py.npy") + err = maximum(abs.(u .- u_py)) + println("边界: $(name) | 最大误差: $(err)") + @assert err < 1e-12 "❌ $(name) 不一致!" +end + +println("✅ 所有边界条件测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01/python/boundary.py b/example/1d-linear-convection/weno3/julia/01/python/boundary.py new file mode 100644 index 00000000..6054f92d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01/python/boundary.py @@ -0,0 +1,103 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01/python/cfd_registry.py b/example/1d-linear-convection/weno3/julia/01/python/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01/python/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01/python/config.py b/example/1d-linear-convection/weno3/julia/01/python/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01/python/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01/python/domain.py b/example/1d-linear-convection/weno3/julia/01/python/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01/python/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01/python/factories/base_factory.py b/example/1d-linear-convection/weno3/julia/01/python/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01/python/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01/python/flux.py b/example/1d-linear-convection/weno3/julia/01/python/flux.py new file mode 100644 index 00000000..beb9ed48 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01/python/flux.py @@ -0,0 +1,73 @@ +""" +通量计算器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册,替代硬编码工厂 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象通量计算基类(统一接口) ---------------------- +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass + +# ---------------------- 2. 具体通量计算子类(使用装饰器注册) ---------------------- + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + +class FluxCalculatorFactory: + """通量计算器工厂""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD对象 + + Returns: + 通量计算器实例 + """ + from factories.base_factory import BaseFactory + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01/python/gen_boundary_test_data.py b/example/1d-linear-convection/weno3/julia/01/python/gen_boundary_test_data.py new file mode 100644 index 00000000..c7fc2a9c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01/python/gen_boundary_test_data.py @@ -0,0 +1,35 @@ +# python/gen_boundary_test_data.py +import numpy as np +from config import CfdConfig +from mesh import Mesh +from domain import Domain + +# 固定测试配置 +config = CfdConfig() +config.with_boundary("dirichlet", left_value=0.5, right_value=1.5) +config.debug = True + +mesh = Mesh() +domain = Domain(config, mesh) + +# 构造 mock CFD 对象(仅含 config + domain) +class MockCfd: + def __init__(self, config, domain): + self.config = config + self.domain = domain + +# 测试用 u:0,1,2,...,N-1 +u_input = np.arange(domain.ntcells, dtype=np.float64) +np.save("u_input.npy", u_input) + +# 测试每种边界 +from boundary import PeriodicBoundary, DirichletBoundary, NeumannBoundary + +for bc_name, bc_class in [("periodic", PeriodicBoundary), ("dirichlet", DirichletBoundary), ("neumann", NeumannBoundary)]: + u = u_input.copy() + cfd_mock = MockCfd(config, domain) + bc = bc_class(cfd_mock) + bc.apply(u) + np.save(f"u_{bc_name}_py.npy", u) + +print("✅ 测试数据已生成:u_*.npy") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01/python/initial_condition.py b/example/1d-linear-convection/weno3/julia/01/python/initial_condition.py new file mode 100644 index 00000000..dabe7e8c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01/python/initial_condition.py @@ -0,0 +1,90 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = self.config.get("pulse_center", 0.5) + width = self.config.get("pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, ic_type: str, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', ic_type, config) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01/python/mesh.py b/example/1d-linear-convection/weno3/julia/01/python/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01/python/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01/python/plotter.py b/example/1d-linear-convection/weno3/julia/01/python/plotter.py new file mode 100644 index 00000000..9f1a414f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01/python/plotter.py @@ -0,0 +1,107 @@ +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01/python/reconstructor/__init__.py b/example/1d-linear-convection/weno3/julia/01/python/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01/python/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01/python/reconstructor/base.py b/example/1d-linear-convection/weno3/julia/01/python/reconstructor/base.py new file mode 100644 index 00000000..bbd63850 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01/python/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def reconstruct(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01/python/reconstructor/eno.py b/example/1d-linear-convection/weno3/julia/01/python/reconstructor/eno.py new file mode 100644 index 00000000..c2fb385d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01/python/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def reconstruct(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01/python/reconstructor/factory.py b/example/1d-linear-convection/weno3/julia/01/python/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01/python/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01/python/reconstructor/weno3.py b/example/1d-linear-convection/weno3/julia/01/python/reconstructor/weno3.py new file mode 100644 index 00000000..6e8c3f23 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01/python/reconstructor/weno3.py @@ -0,0 +1,88 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def reconstruct(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + """ + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 # smoothness indicator for stencil [v2, v3] + beta1 = (v2 - v1)**2 # smoothness indicator for stencil [v1, v2] + + d0, d1 = 2/3, 1/3 # optimal linear weights (for right value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 0.5 * v2 + 0.5 * v3 # reconstruction from [v2, v3] + q1 = -0.5 * v1 + 1.5 * v2 # reconstruction from [v1, v2] + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 + beta1 = (v2 - v1)**2 + + d0, d1 = 1/3, 2/3 # optimal linear weights (for left value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 1.5 * v2 - 0.5 * v3 # from [v2, v3] + q1 = 0.5 * v1 + 0.5 * v2 # from [v1, v2] + return w0 * q0 + w1 * q1 + """ diff --git a/example/1d-linear-convection/weno3/julia/01/python/registry.py b/example/1d-linear-convection/weno3/julia/01/python/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01/python/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01/python/residual.py b/example/1d-linear-convection/weno3/julia/01/python/residual.py new file mode 100644 index 00000000..b4d4d7dc --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01/python/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._reconstruct() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _reconstruct(self): + """私有方法:界面值重建""" + self.reconstructor.reconstruct(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01/python/run_eno_weno.py b/example/1d-linear-convection/weno3/julia/01/python/run_eno_weno.py new file mode 100644 index 00000000..fd7f3874 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01/python/run_eno_weno.py @@ -0,0 +1,52 @@ +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01/python/solution.py b/example/1d-linear-convection/weno3/julia/01/python/solution.py new file mode 100644 index 00000000..92a46d3f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01/python/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config.ic_type, config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01/python/solver.py b/example/1d-linear-convection/weno3/julia/01/python/solver.py new file mode 100644 index 00000000..0d0b442d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01/python/solver.py @@ -0,0 +1,86 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + + +from flux import InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory + +from boundary import BoundaryCondition, PeriodicBoundary, DirichletBoundary, NeumannBoundary, BoundaryConditionFactory + +from time_integration import TimeIntegrator, RK1Integrator, RK2Integrator, RK3Integrator, TimeIntegratorFactory + +from mesh import Mesh + +from reconstructor import ReconstructorFactory + +from initial_condition import InitialCondition, StepFunctionIC, SineWaveIC, GaussianPulseIC, InitialConditionFactory + +from domain import Domain +from solution import Solution + +from config import CfdConfig +from residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config.ic_type, self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + #runge_kutta(self) + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/julia/01/python/time_integration.py b/example/1d-linear-convection/weno3/julia/01/python/time_integration.py new file mode 100644 index 00000000..25ac0b4c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01/python/time_integration.py @@ -0,0 +1,125 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class TimeIntegratorFactory: + """时间推进器工厂""" + + @staticmethod + def create(cfd) -> 'TimeIntegrator': + """ + 创建时间推进器实例 + + Args: + cfd: CFD对象 + + Returns: + 时间推进器实例 + """ + from factories.base_factory import BaseFactory + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01/python/u_dirichlet_py.npy b/example/1d-linear-convection/weno3/julia/01/python/u_dirichlet_py.npy new file mode 100644 index 00000000..3aa20bd2 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01/python/u_dirichlet_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01/python/u_input.npy b/example/1d-linear-convection/weno3/julia/01/python/u_input.npy new file mode 100644 index 00000000..ef506fa7 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01/python/u_input.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01/python/u_neumann_py.npy b/example/1d-linear-convection/weno3/julia/01/python/u_neumann_py.npy new file mode 100644 index 00000000..fa723d1e Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01/python/u_neumann_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01/python/u_periodic_py.npy b/example/1d-linear-convection/weno3/julia/01/python/u_periodic_py.npy new file mode 100644 index 00000000..156aff85 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01/python/u_periodic_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01a/julia/boundary.jl b/example/1d-linear-convection/weno3/julia/01a/julia/boundary.jl new file mode 100644 index 00000000..5b0baaf4 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/julia/boundary.jl @@ -0,0 +1,90 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01a/julia/initial_condition.jl b/example/1d-linear-convection/weno3/julia/01a/julia/initial_condition.jl new file mode 100644 index 00000000..5e029e8d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/julia/initial_condition.jl @@ -0,0 +1,86 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01a/julia/test/test_boundary.jl b/example/1d-linear-convection/weno3/julia/01a/julia/test/test_boundary.jl new file mode 100644 index 00000000..ef27d682 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/julia/test/test_boundary.jl @@ -0,0 +1,57 @@ +# julia/test/test_boundary.jl +using NPZ +include("../boundary.jl") + +struct MockConfig + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + debug::Bool +end + +struct MockDomain + nghosts::Int + ist::Int # Julia 本地索引(1-based) + ied::Int + ntcells::Int +end + +struct MockCfd + config::MockConfig + domain::MockDomain +end + +# ===== 关键:使用 Julia 本地索引规则 ===== +nghosts = 2 +ncells = 40 +ist = nghosts + 1 # = 3 +ied = ist + ncells # = 43 +ntcells = ncells + 2 * nghosts # = 44 + +config = MockConfig("dirichlet", 0.5, 1.5, true) +domain = MockDomain(nghosts, ist, ied, ntcells) +cfd_mock = MockCfd(config, domain) + +# 加载 Python 生成的 u_input.npy +# 注意:Python u[0] → Julia u[1],所以内容完全对应 +u_input = npzread("../../python/u_input.npy") +@assert length(u_input) == ntcells "数组长度不匹配!" + +# 测试三种边界 +test_cases = [ + ("periodic", PeriodicBoundary(cfd_mock)), + ("dirichlet", DirichletBoundary(cfd_mock)), + ("neumann", NeumannBoundary(cfd_mock)) +] + +for (name, bc) in test_cases + u = copy(u_input) + apply!(bc, u) + + u_py = npzread("../../python/u_$(name)_py.npy") + err = maximum(abs.(u .- u_py)) + println("边界: $(name) | 最大误差: $(err)") + @assert err < 1e-12 "❌ $(name) 不一致!" +end + +println("✅ 所有边界条件测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01a/julia/test/test_initial_condition.jl b/example/1d-linear-convection/weno3/julia/01a/julia/test/test_initial_condition.jl new file mode 100644 index 00000000..dc58691a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/julia/test/test_initial_condition.jl @@ -0,0 +1,45 @@ +# julia/test/test_initial_condition.jl +using NPZ + +# 包含初始条件模块 +include("../initial_condition.jl") + +# 构造 mock config(与 Python 完全一致) +struct MockConfig + ic_type::String + domain_length::Float64 + pulse_center::Float64 + pulse_width::Float64 +end + +# 生成与 Python Mesh.xcc 完全相同的 x 坐标 +function generate_xcc() + xmin, xmax = 0.0, 2.0 + ncells = 40 + dx = (xmax - xmin) / ncells + xcc = Vector{Float64}(undef, ncells) + for i in 1:ncells + xcc[i] = xmin + (i - 0.5) * dx # i-1 + 0.5 → i-0.5 + end + return xcc +end + +# 主测试 +xcc = generate_xcc() + +test_cases = [ + ("step", StepFunctionIC(MockConfig("step", 2.0, 0.5, 0.1))), + ("sin", SineWaveIC(MockConfig("sin", 2.0, 0.5, 0.1))), + ("gaussian", GaussianPulseIC(MockConfig("gaussian", 2.0, 0.5, 0.1))) +] + +for (name, ic) in test_cases + u_jl = evaluate_at(ic, xcc) + u_py = npzread("../../python/u_$(name)_interior_py.npy") + + err = maximum(abs.(u_jl .- u_py)) + println("IC: $(name) | 最大误差: $(err)") + @assert err < 1e-12 "❌ $(name) 不一致!" +end + +println("✅ 所有初始条件测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01a/python/boundary.py b/example/1d-linear-convection/weno3/julia/01a/python/boundary.py new file mode 100644 index 00000000..6054f92d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/python/boundary.py @@ -0,0 +1,103 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01a/python/cfd_registry.py b/example/1d-linear-convection/weno3/julia/01a/python/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/python/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01a/python/config.py b/example/1d-linear-convection/weno3/julia/01a/python/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/python/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01a/python/domain.py b/example/1d-linear-convection/weno3/julia/01a/python/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/python/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01a/python/factories/base_factory.py b/example/1d-linear-convection/weno3/julia/01a/python/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/python/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01a/python/flux.py b/example/1d-linear-convection/weno3/julia/01a/python/flux.py new file mode 100644 index 00000000..beb9ed48 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/python/flux.py @@ -0,0 +1,73 @@ +""" +通量计算器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册,替代硬编码工厂 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象通量计算基类(统一接口) ---------------------- +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass + +# ---------------------- 2. 具体通量计算子类(使用装饰器注册) ---------------------- + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + +class FluxCalculatorFactory: + """通量计算器工厂""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD对象 + + Returns: + 通量计算器实例 + """ + from factories.base_factory import BaseFactory + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01a/python/gen_boundary_test_data.py b/example/1d-linear-convection/weno3/julia/01a/python/gen_boundary_test_data.py new file mode 100644 index 00000000..c7fc2a9c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/python/gen_boundary_test_data.py @@ -0,0 +1,35 @@ +# python/gen_boundary_test_data.py +import numpy as np +from config import CfdConfig +from mesh import Mesh +from domain import Domain + +# 固定测试配置 +config = CfdConfig() +config.with_boundary("dirichlet", left_value=0.5, right_value=1.5) +config.debug = True + +mesh = Mesh() +domain = Domain(config, mesh) + +# 构造 mock CFD 对象(仅含 config + domain) +class MockCfd: + def __init__(self, config, domain): + self.config = config + self.domain = domain + +# 测试用 u:0,1,2,...,N-1 +u_input = np.arange(domain.ntcells, dtype=np.float64) +np.save("u_input.npy", u_input) + +# 测试每种边界 +from boundary import PeriodicBoundary, DirichletBoundary, NeumannBoundary + +for bc_name, bc_class in [("periodic", PeriodicBoundary), ("dirichlet", DirichletBoundary), ("neumann", NeumannBoundary)]: + u = u_input.copy() + cfd_mock = MockCfd(config, domain) + bc = bc_class(cfd_mock) + bc.apply(u) + np.save(f"u_{bc_name}_py.npy", u) + +print("✅ 测试数据已生成:u_*.npy") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01a/python/gen_ic_test_data.py b/example/1d-linear-convection/weno3/julia/01a/python/gen_ic_test_data.py new file mode 100644 index 00000000..69c2573b --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/python/gen_ic_test_data.py @@ -0,0 +1,27 @@ +# python/gen_ic_test_data.py +import sys, os +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +import numpy as np +from config import CfdConfig +from mesh import Mesh +from domain import Domain +from solution import Solution + +# 固定 mesh +mesh = Mesh() +config = CfdConfig() + +# 测试三种 IC +for ic_type in ["step", "sin", "gaussian"]: + config.ic_type = ic_type + domain = Domain(config, mesh) + sol = Solution(config, domain) + + u_full = sol.u.copy() # 包含 ghost + u_interior = sol.u[domain.ist:domain.ied].copy() + + np.save(f"u_{ic_type}_full_py.npy", u_full) + np.save(f"u_{ic_type}_interior_py.npy", u_interior) + +print("✅ 初始条件测试数据已生成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01a/python/initial_condition.py b/example/1d-linear-convection/weno3/julia/01a/python/initial_condition.py new file mode 100644 index 00000000..047415b7 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/python/initial_condition.py @@ -0,0 +1,90 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, ic_type: str, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', ic_type, config) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01a/python/mesh.py b/example/1d-linear-convection/weno3/julia/01a/python/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/python/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01a/python/plotter.py b/example/1d-linear-convection/weno3/julia/01a/python/plotter.py new file mode 100644 index 00000000..9f1a414f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/python/plotter.py @@ -0,0 +1,107 @@ +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01a/python/reconstructor/__init__.py b/example/1d-linear-convection/weno3/julia/01a/python/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/python/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01a/python/reconstructor/base.py b/example/1d-linear-convection/weno3/julia/01a/python/reconstructor/base.py new file mode 100644 index 00000000..bbd63850 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/python/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def reconstruct(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01a/python/reconstructor/eno.py b/example/1d-linear-convection/weno3/julia/01a/python/reconstructor/eno.py new file mode 100644 index 00000000..c2fb385d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/python/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def reconstruct(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01a/python/reconstructor/factory.py b/example/1d-linear-convection/weno3/julia/01a/python/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/python/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01a/python/reconstructor/weno3.py b/example/1d-linear-convection/weno3/julia/01a/python/reconstructor/weno3.py new file mode 100644 index 00000000..6e8c3f23 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/python/reconstructor/weno3.py @@ -0,0 +1,88 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def reconstruct(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + """ + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 # smoothness indicator for stencil [v2, v3] + beta1 = (v2 - v1)**2 # smoothness indicator for stencil [v1, v2] + + d0, d1 = 2/3, 1/3 # optimal linear weights (for right value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 0.5 * v2 + 0.5 * v3 # reconstruction from [v2, v3] + q1 = -0.5 * v1 + 1.5 * v2 # reconstruction from [v1, v2] + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 + beta1 = (v2 - v1)**2 + + d0, d1 = 1/3, 2/3 # optimal linear weights (for left value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 1.5 * v2 - 0.5 * v3 # from [v2, v3] + q1 = 0.5 * v1 + 0.5 * v2 # from [v1, v2] + return w0 * q0 + w1 * q1 + """ diff --git a/example/1d-linear-convection/weno3/julia/01a/python/registry.py b/example/1d-linear-convection/weno3/julia/01a/python/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/python/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01a/python/residual.py b/example/1d-linear-convection/weno3/julia/01a/python/residual.py new file mode 100644 index 00000000..b4d4d7dc --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/python/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._reconstruct() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _reconstruct(self): + """私有方法:界面值重建""" + self.reconstructor.reconstruct(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01a/python/run_eno_weno.py b/example/1d-linear-convection/weno3/julia/01a/python/run_eno_weno.py new file mode 100644 index 00000000..fd7f3874 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/python/run_eno_weno.py @@ -0,0 +1,52 @@ +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01a/python/solution.py b/example/1d-linear-convection/weno3/julia/01a/python/solution.py new file mode 100644 index 00000000..92a46d3f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/python/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config.ic_type, config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01a/python/solver.py b/example/1d-linear-convection/weno3/julia/01a/python/solver.py new file mode 100644 index 00000000..0d0b442d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/python/solver.py @@ -0,0 +1,86 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + + +from flux import InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory + +from boundary import BoundaryCondition, PeriodicBoundary, DirichletBoundary, NeumannBoundary, BoundaryConditionFactory + +from time_integration import TimeIntegrator, RK1Integrator, RK2Integrator, RK3Integrator, TimeIntegratorFactory + +from mesh import Mesh + +from reconstructor import ReconstructorFactory + +from initial_condition import InitialCondition, StepFunctionIC, SineWaveIC, GaussianPulseIC, InitialConditionFactory + +from domain import Domain +from solution import Solution + +from config import CfdConfig +from residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config.ic_type, self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + #runge_kutta(self) + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/julia/01a/python/time_integration.py b/example/1d-linear-convection/weno3/julia/01a/python/time_integration.py new file mode 100644 index 00000000..25ac0b4c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01a/python/time_integration.py @@ -0,0 +1,125 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class TimeIntegratorFactory: + """时间推进器工厂""" + + @staticmethod + def create(cfd) -> 'TimeIntegrator': + """ + 创建时间推进器实例 + + Args: + cfd: CFD对象 + + Returns: + 时间推进器实例 + """ + from factories.base_factory import BaseFactory + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01a/python/u_dirichlet_py.npy b/example/1d-linear-convection/weno3/julia/01a/python/u_dirichlet_py.npy new file mode 100644 index 00000000..3aa20bd2 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01a/python/u_dirichlet_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01a/python/u_gaussian_full_py.npy b/example/1d-linear-convection/weno3/julia/01a/python/u_gaussian_full_py.npy new file mode 100644 index 00000000..b762f0fe Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01a/python/u_gaussian_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01a/python/u_gaussian_interior_py.npy b/example/1d-linear-convection/weno3/julia/01a/python/u_gaussian_interior_py.npy new file mode 100644 index 00000000..68af8954 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01a/python/u_gaussian_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01a/python/u_input.npy b/example/1d-linear-convection/weno3/julia/01a/python/u_input.npy new file mode 100644 index 00000000..ef506fa7 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01a/python/u_input.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01a/python/u_neumann_py.npy b/example/1d-linear-convection/weno3/julia/01a/python/u_neumann_py.npy new file mode 100644 index 00000000..fa723d1e Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01a/python/u_neumann_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01a/python/u_periodic_py.npy b/example/1d-linear-convection/weno3/julia/01a/python/u_periodic_py.npy new file mode 100644 index 00000000..156aff85 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01a/python/u_periodic_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01a/python/u_sin_full_py.npy b/example/1d-linear-convection/weno3/julia/01a/python/u_sin_full_py.npy new file mode 100644 index 00000000..b87f8a13 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01a/python/u_sin_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01a/python/u_sin_interior_py.npy b/example/1d-linear-convection/weno3/julia/01a/python/u_sin_interior_py.npy new file mode 100644 index 00000000..6803fbfb Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01a/python/u_sin_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01a/python/u_step_full_py.npy b/example/1d-linear-convection/weno3/julia/01a/python/u_step_full_py.npy new file mode 100644 index 00000000..2fc0e183 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01a/python/u_step_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01a/python/u_step_interior_py.npy b/example/1d-linear-convection/weno3/julia/01a/python/u_step_interior_py.npy new file mode 100644 index 00000000..b5ad6600 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01a/python/u_step_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01b/julia/boundary.jl b/example/1d-linear-convection/weno3/julia/01b/julia/boundary.jl new file mode 100644 index 00000000..5b0baaf4 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/julia/boundary.jl @@ -0,0 +1,90 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/julia/initial_condition.jl b/example/1d-linear-convection/weno3/julia/01b/julia/initial_condition.jl new file mode 100644 index 00000000..5e029e8d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/julia/initial_condition.jl @@ -0,0 +1,86 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/julia/mesh.jl b/example/1d-linear-convection/weno3/julia/01b/julia/mesh.jl new file mode 100644 index 00000000..1b0dd4bf --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/julia/mesh.jl @@ -0,0 +1,45 @@ +# julia/mesh.jl +""" +Mesh 结构体(与提供的 mesh.py 完全同构) +- 字段名、初始化顺序、循环逻辑完全一致 +- 不做任何泛化或优化 +""" + +mutable struct Mesh + xmin::Float64 + xmax::Float64 + ncells::Int + nnodes::Int + nx::Int + x::Vector{Float64} + xcc::Vector{Float64} + L::Float64 # 在 init_mesh 中赋值 + dx::Float64 # 在 init_mesh 中赋值 + + function Mesh() + # 与 Python __init__ 完全一致 + xmin = 0.0 + xmax = 2.0 + ncells = 40 + nnodes = ncells + 1 + nx = ncells + x = zeros(Float64, nnodes) + xcc = zeros(Float64, ncells) + + # 调用 init_mesh(模拟 Python) + L = xmax - xmin + dx = L / ncells + + # Generate node coordinates: for i in range(self.nnodes) + for i in 1:nnodes + x[i] = xmin + (i - 1) * dx # Python i → Julia i-1 + end + + # Generate cell center coordinates + for i in 1:ncells + xcc[i] = 0.5 * (x[i] + x[i+1]) + end + + new(xmin, xmax, ncells, nnodes, nx, x, xcc, L, dx) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/julia/test/test_boundary.jl b/example/1d-linear-convection/weno3/julia/01b/julia/test/test_boundary.jl new file mode 100644 index 00000000..ef27d682 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/julia/test/test_boundary.jl @@ -0,0 +1,57 @@ +# julia/test/test_boundary.jl +using NPZ +include("../boundary.jl") + +struct MockConfig + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + debug::Bool +end + +struct MockDomain + nghosts::Int + ist::Int # Julia 本地索引(1-based) + ied::Int + ntcells::Int +end + +struct MockCfd + config::MockConfig + domain::MockDomain +end + +# ===== 关键:使用 Julia 本地索引规则 ===== +nghosts = 2 +ncells = 40 +ist = nghosts + 1 # = 3 +ied = ist + ncells # = 43 +ntcells = ncells + 2 * nghosts # = 44 + +config = MockConfig("dirichlet", 0.5, 1.5, true) +domain = MockDomain(nghosts, ist, ied, ntcells) +cfd_mock = MockCfd(config, domain) + +# 加载 Python 生成的 u_input.npy +# 注意:Python u[0] → Julia u[1],所以内容完全对应 +u_input = npzread("../../python/u_input.npy") +@assert length(u_input) == ntcells "数组长度不匹配!" + +# 测试三种边界 +test_cases = [ + ("periodic", PeriodicBoundary(cfd_mock)), + ("dirichlet", DirichletBoundary(cfd_mock)), + ("neumann", NeumannBoundary(cfd_mock)) +] + +for (name, bc) in test_cases + u = copy(u_input) + apply!(bc, u) + + u_py = npzread("../../python/u_$(name)_py.npy") + err = maximum(abs.(u .- u_py)) + println("边界: $(name) | 最大误差: $(err)") + @assert err < 1e-12 "❌ $(name) 不一致!" +end + +println("✅ 所有边界条件测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/julia/test/test_initial_condition.jl b/example/1d-linear-convection/weno3/julia/01b/julia/test/test_initial_condition.jl new file mode 100644 index 00000000..dc58691a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/julia/test/test_initial_condition.jl @@ -0,0 +1,45 @@ +# julia/test/test_initial_condition.jl +using NPZ + +# 包含初始条件模块 +include("../initial_condition.jl") + +# 构造 mock config(与 Python 完全一致) +struct MockConfig + ic_type::String + domain_length::Float64 + pulse_center::Float64 + pulse_width::Float64 +end + +# 生成与 Python Mesh.xcc 完全相同的 x 坐标 +function generate_xcc() + xmin, xmax = 0.0, 2.0 + ncells = 40 + dx = (xmax - xmin) / ncells + xcc = Vector{Float64}(undef, ncells) + for i in 1:ncells + xcc[i] = xmin + (i - 0.5) * dx # i-1 + 0.5 → i-0.5 + end + return xcc +end + +# 主测试 +xcc = generate_xcc() + +test_cases = [ + ("step", StepFunctionIC(MockConfig("step", 2.0, 0.5, 0.1))), + ("sin", SineWaveIC(MockConfig("sin", 2.0, 0.5, 0.1))), + ("gaussian", GaussianPulseIC(MockConfig("gaussian", 2.0, 0.5, 0.1))) +] + +for (name, ic) in test_cases + u_jl = evaluate_at(ic, xcc) + u_py = npzread("../../python/u_$(name)_interior_py.npy") + + err = maximum(abs.(u_jl .- u_py)) + println("IC: $(name) | 最大误差: $(err)") + @assert err < 1e-12 "❌ $(name) 不一致!" +end + +println("✅ 所有初始条件测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/julia/test/test_mesh.jl b/example/1d-linear-convection/weno3/julia/01b/julia/test/test_mesh.jl new file mode 100644 index 00000000..315d2aac --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/julia/test/test_mesh.jl @@ -0,0 +1,37 @@ +# julia/test/test_mesh.jl +include("../mesh.jl") + +# 创建 mesh(与 Python 完全相同) +mesh = Mesh() + +# 打印关键值(与 Python 对比) +println("xmin = ", mesh.xmin) # 0.0 +println("xmax = ", mesh.xmax) # 2.0 +println("ncells = ", mesh.ncells) # 40 +println("nnodes = ", mesh.nnodes) # 41 +println("nx = ", mesh.nx) # 40 +println("L = ", mesh.L) # 2.0 +println("dx = ", mesh.dx) # 0.05 + +# 检查 x[1] (Python x[0]) 和 x[41] (Python x[40]) +println("x[1] = ", mesh.x[1]) # 0.0 +println("x[41] = ", mesh.x[41]) # 2.0 + +# 检查 xcc[1] (Python xcc[0]) 和 xcc[40] (Python xcc[39]) +println("xcc[1] = ", mesh.xcc[1]) # 0.025 +println("xcc[40] = ", mesh.xcc[40]) # 1.975 + +# ✅ 严格断言 +@assert mesh.xmin == 0.0 +@assert mesh.xmax == 2.0 +@assert mesh.ncells == 40 +@assert mesh.nnodes == 41 +@assert mesh.nx == 40 +@assert mesh.L == 2.0 +@assert mesh.dx == 0.05 +@assert mesh.x[1] == 0.0 +@assert mesh.x[41] == 2.0 +@assert abs(mesh.xcc[1] - 0.025) < 1e-12 +@assert abs(mesh.xcc[40] - 1.975) < 1e-12 + +println("✅ Mesh 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/python/boundary.py b/example/1d-linear-convection/weno3/julia/01b/python/boundary.py new file mode 100644 index 00000000..6054f92d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/python/boundary.py @@ -0,0 +1,103 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/python/cfd_registry.py b/example/1d-linear-convection/weno3/julia/01b/python/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/python/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/python/config.py b/example/1d-linear-convection/weno3/julia/01b/python/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/python/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/python/domain.py b/example/1d-linear-convection/weno3/julia/01b/python/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/python/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/python/factories/base_factory.py b/example/1d-linear-convection/weno3/julia/01b/python/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/python/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/python/flux.py b/example/1d-linear-convection/weno3/julia/01b/python/flux.py new file mode 100644 index 00000000..beb9ed48 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/python/flux.py @@ -0,0 +1,73 @@ +""" +通量计算器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册,替代硬编码工厂 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象通量计算基类(统一接口) ---------------------- +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass + +# ---------------------- 2. 具体通量计算子类(使用装饰器注册) ---------------------- + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + +class FluxCalculatorFactory: + """通量计算器工厂""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD对象 + + Returns: + 通量计算器实例 + """ + from factories.base_factory import BaseFactory + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/python/gen_boundary_test_data.py b/example/1d-linear-convection/weno3/julia/01b/python/gen_boundary_test_data.py new file mode 100644 index 00000000..c7fc2a9c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/python/gen_boundary_test_data.py @@ -0,0 +1,35 @@ +# python/gen_boundary_test_data.py +import numpy as np +from config import CfdConfig +from mesh import Mesh +from domain import Domain + +# 固定测试配置 +config = CfdConfig() +config.with_boundary("dirichlet", left_value=0.5, right_value=1.5) +config.debug = True + +mesh = Mesh() +domain = Domain(config, mesh) + +# 构造 mock CFD 对象(仅含 config + domain) +class MockCfd: + def __init__(self, config, domain): + self.config = config + self.domain = domain + +# 测试用 u:0,1,2,...,N-1 +u_input = np.arange(domain.ntcells, dtype=np.float64) +np.save("u_input.npy", u_input) + +# 测试每种边界 +from boundary import PeriodicBoundary, DirichletBoundary, NeumannBoundary + +for bc_name, bc_class in [("periodic", PeriodicBoundary), ("dirichlet", DirichletBoundary), ("neumann", NeumannBoundary)]: + u = u_input.copy() + cfd_mock = MockCfd(config, domain) + bc = bc_class(cfd_mock) + bc.apply(u) + np.save(f"u_{bc_name}_py.npy", u) + +print("✅ 测试数据已生成:u_*.npy") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/python/gen_ic_test_data.py b/example/1d-linear-convection/weno3/julia/01b/python/gen_ic_test_data.py new file mode 100644 index 00000000..69c2573b --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/python/gen_ic_test_data.py @@ -0,0 +1,27 @@ +# python/gen_ic_test_data.py +import sys, os +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +import numpy as np +from config import CfdConfig +from mesh import Mesh +from domain import Domain +from solution import Solution + +# 固定 mesh +mesh = Mesh() +config = CfdConfig() + +# 测试三种 IC +for ic_type in ["step", "sin", "gaussian"]: + config.ic_type = ic_type + domain = Domain(config, mesh) + sol = Solution(config, domain) + + u_full = sol.u.copy() # 包含 ghost + u_interior = sol.u[domain.ist:domain.ied].copy() + + np.save(f"u_{ic_type}_full_py.npy", u_full) + np.save(f"u_{ic_type}_interior_py.npy", u_interior) + +print("✅ 初始条件测试数据已生成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/python/initial_condition.py b/example/1d-linear-convection/weno3/julia/01b/python/initial_condition.py new file mode 100644 index 00000000..047415b7 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/python/initial_condition.py @@ -0,0 +1,90 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, ic_type: str, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', ic_type, config) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/python/mesh.py b/example/1d-linear-convection/weno3/julia/01b/python/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/python/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/python/plotter.py b/example/1d-linear-convection/weno3/julia/01b/python/plotter.py new file mode 100644 index 00000000..9f1a414f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/python/plotter.py @@ -0,0 +1,107 @@ +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/python/reconstructor/__init__.py b/example/1d-linear-convection/weno3/julia/01b/python/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/python/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/python/reconstructor/base.py b/example/1d-linear-convection/weno3/julia/01b/python/reconstructor/base.py new file mode 100644 index 00000000..bbd63850 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/python/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def reconstruct(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/python/reconstructor/eno.py b/example/1d-linear-convection/weno3/julia/01b/python/reconstructor/eno.py new file mode 100644 index 00000000..c2fb385d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/python/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def reconstruct(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/python/reconstructor/factory.py b/example/1d-linear-convection/weno3/julia/01b/python/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/python/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/python/reconstructor/weno3.py b/example/1d-linear-convection/weno3/julia/01b/python/reconstructor/weno3.py new file mode 100644 index 00000000..6e8c3f23 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/python/reconstructor/weno3.py @@ -0,0 +1,88 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def reconstruct(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + """ + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 # smoothness indicator for stencil [v2, v3] + beta1 = (v2 - v1)**2 # smoothness indicator for stencil [v1, v2] + + d0, d1 = 2/3, 1/3 # optimal linear weights (for right value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 0.5 * v2 + 0.5 * v3 # reconstruction from [v2, v3] + q1 = -0.5 * v1 + 1.5 * v2 # reconstruction from [v1, v2] + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 + beta1 = (v2 - v1)**2 + + d0, d1 = 1/3, 2/3 # optimal linear weights (for left value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 1.5 * v2 - 0.5 * v3 # from [v2, v3] + q1 = 0.5 * v1 + 0.5 * v2 # from [v1, v2] + return w0 * q0 + w1 * q1 + """ diff --git a/example/1d-linear-convection/weno3/julia/01b/python/registry.py b/example/1d-linear-convection/weno3/julia/01b/python/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/python/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/python/residual.py b/example/1d-linear-convection/weno3/julia/01b/python/residual.py new file mode 100644 index 00000000..b4d4d7dc --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/python/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._reconstruct() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _reconstruct(self): + """私有方法:界面值重建""" + self.reconstructor.reconstruct(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/python/run_eno_weno.py b/example/1d-linear-convection/weno3/julia/01b/python/run_eno_weno.py new file mode 100644 index 00000000..fd7f3874 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/python/run_eno_weno.py @@ -0,0 +1,52 @@ +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/python/solution.py b/example/1d-linear-convection/weno3/julia/01b/python/solution.py new file mode 100644 index 00000000..92a46d3f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/python/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config.ic_type, config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/python/solver.py b/example/1d-linear-convection/weno3/julia/01b/python/solver.py new file mode 100644 index 00000000..0d0b442d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/python/solver.py @@ -0,0 +1,86 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + + +from flux import InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory + +from boundary import BoundaryCondition, PeriodicBoundary, DirichletBoundary, NeumannBoundary, BoundaryConditionFactory + +from time_integration import TimeIntegrator, RK1Integrator, RK2Integrator, RK3Integrator, TimeIntegratorFactory + +from mesh import Mesh + +from reconstructor import ReconstructorFactory + +from initial_condition import InitialCondition, StepFunctionIC, SineWaveIC, GaussianPulseIC, InitialConditionFactory + +from domain import Domain +from solution import Solution + +from config import CfdConfig +from residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config.ic_type, self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + #runge_kutta(self) + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/julia/01b/python/time_integration.py b/example/1d-linear-convection/weno3/julia/01b/python/time_integration.py new file mode 100644 index 00000000..25ac0b4c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01b/python/time_integration.py @@ -0,0 +1,125 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class TimeIntegratorFactory: + """时间推进器工厂""" + + @staticmethod + def create(cfd) -> 'TimeIntegrator': + """ + 创建时间推进器实例 + + Args: + cfd: CFD对象 + + Returns: + 时间推进器实例 + """ + from factories.base_factory import BaseFactory + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01b/python/u_dirichlet_py.npy b/example/1d-linear-convection/weno3/julia/01b/python/u_dirichlet_py.npy new file mode 100644 index 00000000..3aa20bd2 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01b/python/u_dirichlet_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01b/python/u_gaussian_full_py.npy b/example/1d-linear-convection/weno3/julia/01b/python/u_gaussian_full_py.npy new file mode 100644 index 00000000..b762f0fe Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01b/python/u_gaussian_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01b/python/u_gaussian_interior_py.npy b/example/1d-linear-convection/weno3/julia/01b/python/u_gaussian_interior_py.npy new file mode 100644 index 00000000..68af8954 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01b/python/u_gaussian_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01b/python/u_input.npy b/example/1d-linear-convection/weno3/julia/01b/python/u_input.npy new file mode 100644 index 00000000..ef506fa7 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01b/python/u_input.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01b/python/u_neumann_py.npy b/example/1d-linear-convection/weno3/julia/01b/python/u_neumann_py.npy new file mode 100644 index 00000000..fa723d1e Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01b/python/u_neumann_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01b/python/u_periodic_py.npy b/example/1d-linear-convection/weno3/julia/01b/python/u_periodic_py.npy new file mode 100644 index 00000000..156aff85 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01b/python/u_periodic_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01b/python/u_sin_full_py.npy b/example/1d-linear-convection/weno3/julia/01b/python/u_sin_full_py.npy new file mode 100644 index 00000000..b87f8a13 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01b/python/u_sin_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01b/python/u_sin_interior_py.npy b/example/1d-linear-convection/weno3/julia/01b/python/u_sin_interior_py.npy new file mode 100644 index 00000000..6803fbfb Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01b/python/u_sin_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01b/python/u_step_full_py.npy b/example/1d-linear-convection/weno3/julia/01b/python/u_step_full_py.npy new file mode 100644 index 00000000..2fc0e183 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01b/python/u_step_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01b/python/u_step_interior_py.npy b/example/1d-linear-convection/weno3/julia/01b/python/u_step_interior_py.npy new file mode 100644 index 00000000..b5ad6600 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01b/python/u_step_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01c/julia/boundary.jl b/example/1d-linear-convection/weno3/julia/01c/julia/boundary.jl new file mode 100644 index 00000000..5b0baaf4 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/julia/boundary.jl @@ -0,0 +1,90 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/julia/domain.jl b/example/1d-linear-convection/weno3/julia/01c/julia/domain.jl new file mode 100644 index 00000000..818be8fa --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/julia/domain.jl @@ -0,0 +1,61 @@ +# julia/domain.jl +""" +Domain 结构体(与 domain.py 完全同构) +- 保存 config +- nghosts 计算逻辑 1:1 +- ist/ied 为 0-based(与 Python 一致) +""" + +include("mesh.jl") + +mutable struct Domain + config::Any # 保存 config(与 Python 一致) + mesh::Mesh + nghosts::Int + ist::Int # 0-based + ied::Int # 0-based + ntcells::Int +end + +function Domain(config::Any, mesh::Mesh) + nghosts = _calc_nghosts(config) + ist = nghosts + ied = ist + mesh.ncells + ntcells = mesh.ncells + 2 * nghosts + Domain(config, mesh, nghosts, ist, ied, ntcells) +end + +function _calc_nghosts(config::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + order = getfield_safe(config, :spatial_order, 2) + + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + if scheme == "eno" + nghosts = order + elseif startswith(scheme, "weno") + nghosts = order ÷ 2 + 1 + else + error("未知重建格式 $(scheme),无法计算ghost层!") + end + + if nghosts <= 0 + error("计算得到的ghost层数量无效:$(nghosts)(阶数$(order),格式$(scheme))") + end + + return nghosts +end + +function is_physical_cell(domain::Domain, idx::Int) + return domain.ist <= idx < domain.ied +end + +function get_physical_indices(domain::Domain) + return domain.ist:(domain.ied - 1) +end + +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/julia/initial_condition.jl b/example/1d-linear-convection/weno3/julia/01c/julia/initial_condition.jl new file mode 100644 index 00000000..5e029e8d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/julia/initial_condition.jl @@ -0,0 +1,86 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/julia/mesh.jl b/example/1d-linear-convection/weno3/julia/01c/julia/mesh.jl new file mode 100644 index 00000000..1b0dd4bf --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/julia/mesh.jl @@ -0,0 +1,45 @@ +# julia/mesh.jl +""" +Mesh 结构体(与提供的 mesh.py 完全同构) +- 字段名、初始化顺序、循环逻辑完全一致 +- 不做任何泛化或优化 +""" + +mutable struct Mesh + xmin::Float64 + xmax::Float64 + ncells::Int + nnodes::Int + nx::Int + x::Vector{Float64} + xcc::Vector{Float64} + L::Float64 # 在 init_mesh 中赋值 + dx::Float64 # 在 init_mesh 中赋值 + + function Mesh() + # 与 Python __init__ 完全一致 + xmin = 0.0 + xmax = 2.0 + ncells = 40 + nnodes = ncells + 1 + nx = ncells + x = zeros(Float64, nnodes) + xcc = zeros(Float64, ncells) + + # 调用 init_mesh(模拟 Python) + L = xmax - xmin + dx = L / ncells + + # Generate node coordinates: for i in range(self.nnodes) + for i in 1:nnodes + x[i] = xmin + (i - 1) * dx # Python i → Julia i-1 + end + + # Generate cell center coordinates + for i in 1:ncells + xcc[i] = 0.5 * (x[i] + x[i+1]) + end + + new(xmin, xmax, ncells, nnodes, nx, x, xcc, L, dx) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/julia/test/test_boundary.jl b/example/1d-linear-convection/weno3/julia/01c/julia/test/test_boundary.jl new file mode 100644 index 00000000..ef27d682 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/julia/test/test_boundary.jl @@ -0,0 +1,57 @@ +# julia/test/test_boundary.jl +using NPZ +include("../boundary.jl") + +struct MockConfig + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + debug::Bool +end + +struct MockDomain + nghosts::Int + ist::Int # Julia 本地索引(1-based) + ied::Int + ntcells::Int +end + +struct MockCfd + config::MockConfig + domain::MockDomain +end + +# ===== 关键:使用 Julia 本地索引规则 ===== +nghosts = 2 +ncells = 40 +ist = nghosts + 1 # = 3 +ied = ist + ncells # = 43 +ntcells = ncells + 2 * nghosts # = 44 + +config = MockConfig("dirichlet", 0.5, 1.5, true) +domain = MockDomain(nghosts, ist, ied, ntcells) +cfd_mock = MockCfd(config, domain) + +# 加载 Python 生成的 u_input.npy +# 注意:Python u[0] → Julia u[1],所以内容完全对应 +u_input = npzread("../../python/u_input.npy") +@assert length(u_input) == ntcells "数组长度不匹配!" + +# 测试三种边界 +test_cases = [ + ("periodic", PeriodicBoundary(cfd_mock)), + ("dirichlet", DirichletBoundary(cfd_mock)), + ("neumann", NeumannBoundary(cfd_mock)) +] + +for (name, bc) in test_cases + u = copy(u_input) + apply!(bc, u) + + u_py = npzread("../../python/u_$(name)_py.npy") + err = maximum(abs.(u .- u_py)) + println("边界: $(name) | 最大误差: $(err)") + @assert err < 1e-12 "❌ $(name) 不一致!" +end + +println("✅ 所有边界条件测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/julia/test/test_domain.jl b/example/1d-linear-convection/weno3/julia/01c/julia/test/test_domain.jl new file mode 100644 index 00000000..7b423f77 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/julia/test/test_domain.jl @@ -0,0 +1,44 @@ +# julia/test/test_domain.jl +include("../mesh.jl") +include("../domain.jl") + +# MockConfig:模拟 Python CfdConfig +struct MockConfig + recon_scheme::String + spatial_order::Int +end + +# 测试 ENO +config_eno = MockConfig("eno", 2) +mesh = Mesh() +domain_eno = Domain(config_eno, mesh) + +println("ENO: nghosts = ", domain_eno.nghosts) # 2 +println("ENO: ist = ", domain_eno.ist) # 2 +println("ENO: ied = ", domain_eno.ied) # 42 +println("物理索引范围: ", collect(get_physical_indices(domain_eno))[1:3], " ... ", collect(get_physical_indices(domain_eno))[end-2:end]) + +# 测试 WENO(字符串 "weno") +config_weno = MockConfig("weno", 2) +domain_weno = Domain(config_weno, mesh) +println("WENO: nghosts = ", domain_weno.nghosts) # 2 + +# 测试 WENO3(字符串 "weno3") +config_weno3 = MockConfig("weno3", 2) +domain_weno3 = Domain(config_weno3, mesh) +println("WENO3: nghosts = ", domain_weno3.nghosts) # 2 + +# 测试 is_physical_cell +@assert is_physical_cell(domain_eno, 2) == true # ist=2 +@assert is_physical_cell(domain_eno, 41) == true # ied-1=41 +@assert is_physical_cell(domain_eno, 42) == false # ied=42 + +# ✅ 断言 +@assert domain_eno.nghosts == 2 +@assert domain_eno.ist == 2 +@assert domain_eno.ied == 42 +@assert domain_eno.ntcells == 44 +@assert domain_weno.nghosts == 2 +@assert domain_weno3.nghosts == 2 + +println("✅ Domain 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/julia/test/test_initial_condition.jl b/example/1d-linear-convection/weno3/julia/01c/julia/test/test_initial_condition.jl new file mode 100644 index 00000000..dc58691a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/julia/test/test_initial_condition.jl @@ -0,0 +1,45 @@ +# julia/test/test_initial_condition.jl +using NPZ + +# 包含初始条件模块 +include("../initial_condition.jl") + +# 构造 mock config(与 Python 完全一致) +struct MockConfig + ic_type::String + domain_length::Float64 + pulse_center::Float64 + pulse_width::Float64 +end + +# 生成与 Python Mesh.xcc 完全相同的 x 坐标 +function generate_xcc() + xmin, xmax = 0.0, 2.0 + ncells = 40 + dx = (xmax - xmin) / ncells + xcc = Vector{Float64}(undef, ncells) + for i in 1:ncells + xcc[i] = xmin + (i - 0.5) * dx # i-1 + 0.5 → i-0.5 + end + return xcc +end + +# 主测试 +xcc = generate_xcc() + +test_cases = [ + ("step", StepFunctionIC(MockConfig("step", 2.0, 0.5, 0.1))), + ("sin", SineWaveIC(MockConfig("sin", 2.0, 0.5, 0.1))), + ("gaussian", GaussianPulseIC(MockConfig("gaussian", 2.0, 0.5, 0.1))) +] + +for (name, ic) in test_cases + u_jl = evaluate_at(ic, xcc) + u_py = npzread("../../python/u_$(name)_interior_py.npy") + + err = maximum(abs.(u_jl .- u_py)) + println("IC: $(name) | 最大误差: $(err)") + @assert err < 1e-12 "❌ $(name) 不一致!" +end + +println("✅ 所有初始条件测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/julia/test/test_mesh.jl b/example/1d-linear-convection/weno3/julia/01c/julia/test/test_mesh.jl new file mode 100644 index 00000000..315d2aac --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/julia/test/test_mesh.jl @@ -0,0 +1,37 @@ +# julia/test/test_mesh.jl +include("../mesh.jl") + +# 创建 mesh(与 Python 完全相同) +mesh = Mesh() + +# 打印关键值(与 Python 对比) +println("xmin = ", mesh.xmin) # 0.0 +println("xmax = ", mesh.xmax) # 2.0 +println("ncells = ", mesh.ncells) # 40 +println("nnodes = ", mesh.nnodes) # 41 +println("nx = ", mesh.nx) # 40 +println("L = ", mesh.L) # 2.0 +println("dx = ", mesh.dx) # 0.05 + +# 检查 x[1] (Python x[0]) 和 x[41] (Python x[40]) +println("x[1] = ", mesh.x[1]) # 0.0 +println("x[41] = ", mesh.x[41]) # 2.0 + +# 检查 xcc[1] (Python xcc[0]) 和 xcc[40] (Python xcc[39]) +println("xcc[1] = ", mesh.xcc[1]) # 0.025 +println("xcc[40] = ", mesh.xcc[40]) # 1.975 + +# ✅ 严格断言 +@assert mesh.xmin == 0.0 +@assert mesh.xmax == 2.0 +@assert mesh.ncells == 40 +@assert mesh.nnodes == 41 +@assert mesh.nx == 40 +@assert mesh.L == 2.0 +@assert mesh.dx == 0.05 +@assert mesh.x[1] == 0.0 +@assert mesh.x[41] == 2.0 +@assert abs(mesh.xcc[1] - 0.025) < 1e-12 +@assert abs(mesh.xcc[40] - 1.975) < 1e-12 + +println("✅ Mesh 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/python/boundary.py b/example/1d-linear-convection/weno3/julia/01c/python/boundary.py new file mode 100644 index 00000000..6054f92d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/python/boundary.py @@ -0,0 +1,103 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/python/cfd_registry.py b/example/1d-linear-convection/weno3/julia/01c/python/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/python/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/python/config.py b/example/1d-linear-convection/weno3/julia/01c/python/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/python/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/python/domain.py b/example/1d-linear-convection/weno3/julia/01c/python/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/python/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/python/factories/base_factory.py b/example/1d-linear-convection/weno3/julia/01c/python/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/python/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/python/flux.py b/example/1d-linear-convection/weno3/julia/01c/python/flux.py new file mode 100644 index 00000000..beb9ed48 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/python/flux.py @@ -0,0 +1,73 @@ +""" +通量计算器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册,替代硬编码工厂 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象通量计算基类(统一接口) ---------------------- +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass + +# ---------------------- 2. 具体通量计算子类(使用装饰器注册) ---------------------- + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + +class FluxCalculatorFactory: + """通量计算器工厂""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD对象 + + Returns: + 通量计算器实例 + """ + from factories.base_factory import BaseFactory + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/python/gen_boundary_test_data.py b/example/1d-linear-convection/weno3/julia/01c/python/gen_boundary_test_data.py new file mode 100644 index 00000000..c7fc2a9c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/python/gen_boundary_test_data.py @@ -0,0 +1,35 @@ +# python/gen_boundary_test_data.py +import numpy as np +from config import CfdConfig +from mesh import Mesh +from domain import Domain + +# 固定测试配置 +config = CfdConfig() +config.with_boundary("dirichlet", left_value=0.5, right_value=1.5) +config.debug = True + +mesh = Mesh() +domain = Domain(config, mesh) + +# 构造 mock CFD 对象(仅含 config + domain) +class MockCfd: + def __init__(self, config, domain): + self.config = config + self.domain = domain + +# 测试用 u:0,1,2,...,N-1 +u_input = np.arange(domain.ntcells, dtype=np.float64) +np.save("u_input.npy", u_input) + +# 测试每种边界 +from boundary import PeriodicBoundary, DirichletBoundary, NeumannBoundary + +for bc_name, bc_class in [("periodic", PeriodicBoundary), ("dirichlet", DirichletBoundary), ("neumann", NeumannBoundary)]: + u = u_input.copy() + cfd_mock = MockCfd(config, domain) + bc = bc_class(cfd_mock) + bc.apply(u) + np.save(f"u_{bc_name}_py.npy", u) + +print("✅ 测试数据已生成:u_*.npy") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/python/gen_ic_test_data.py b/example/1d-linear-convection/weno3/julia/01c/python/gen_ic_test_data.py new file mode 100644 index 00000000..69c2573b --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/python/gen_ic_test_data.py @@ -0,0 +1,27 @@ +# python/gen_ic_test_data.py +import sys, os +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +import numpy as np +from config import CfdConfig +from mesh import Mesh +from domain import Domain +from solution import Solution + +# 固定 mesh +mesh = Mesh() +config = CfdConfig() + +# 测试三种 IC +for ic_type in ["step", "sin", "gaussian"]: + config.ic_type = ic_type + domain = Domain(config, mesh) + sol = Solution(config, domain) + + u_full = sol.u.copy() # 包含 ghost + u_interior = sol.u[domain.ist:domain.ied].copy() + + np.save(f"u_{ic_type}_full_py.npy", u_full) + np.save(f"u_{ic_type}_interior_py.npy", u_interior) + +print("✅ 初始条件测试数据已生成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/python/initial_condition.py b/example/1d-linear-convection/weno3/julia/01c/python/initial_condition.py new file mode 100644 index 00000000..047415b7 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/python/initial_condition.py @@ -0,0 +1,90 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, ic_type: str, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', ic_type, config) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/python/mesh.py b/example/1d-linear-convection/weno3/julia/01c/python/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/python/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/python/plotter.py b/example/1d-linear-convection/weno3/julia/01c/python/plotter.py new file mode 100644 index 00000000..9f1a414f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/python/plotter.py @@ -0,0 +1,107 @@ +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/python/reconstructor/__init__.py b/example/1d-linear-convection/weno3/julia/01c/python/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/python/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/python/reconstructor/base.py b/example/1d-linear-convection/weno3/julia/01c/python/reconstructor/base.py new file mode 100644 index 00000000..bbd63850 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/python/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def reconstruct(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/python/reconstructor/eno.py b/example/1d-linear-convection/weno3/julia/01c/python/reconstructor/eno.py new file mode 100644 index 00000000..c2fb385d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/python/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def reconstruct(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/python/reconstructor/factory.py b/example/1d-linear-convection/weno3/julia/01c/python/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/python/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/python/reconstructor/weno3.py b/example/1d-linear-convection/weno3/julia/01c/python/reconstructor/weno3.py new file mode 100644 index 00000000..6e8c3f23 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/python/reconstructor/weno3.py @@ -0,0 +1,88 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def reconstruct(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + """ + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 # smoothness indicator for stencil [v2, v3] + beta1 = (v2 - v1)**2 # smoothness indicator for stencil [v1, v2] + + d0, d1 = 2/3, 1/3 # optimal linear weights (for right value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 0.5 * v2 + 0.5 * v3 # reconstruction from [v2, v3] + q1 = -0.5 * v1 + 1.5 * v2 # reconstruction from [v1, v2] + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 + beta1 = (v2 - v1)**2 + + d0, d1 = 1/3, 2/3 # optimal linear weights (for left value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 1.5 * v2 - 0.5 * v3 # from [v2, v3] + q1 = 0.5 * v1 + 0.5 * v2 # from [v1, v2] + return w0 * q0 + w1 * q1 + """ diff --git a/example/1d-linear-convection/weno3/julia/01c/python/registry.py b/example/1d-linear-convection/weno3/julia/01c/python/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/python/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/python/residual.py b/example/1d-linear-convection/weno3/julia/01c/python/residual.py new file mode 100644 index 00000000..b4d4d7dc --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/python/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._reconstruct() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _reconstruct(self): + """私有方法:界面值重建""" + self.reconstructor.reconstruct(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/python/run_eno_weno.py b/example/1d-linear-convection/weno3/julia/01c/python/run_eno_weno.py new file mode 100644 index 00000000..fd7f3874 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/python/run_eno_weno.py @@ -0,0 +1,52 @@ +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/python/solution.py b/example/1d-linear-convection/weno3/julia/01c/python/solution.py new file mode 100644 index 00000000..92a46d3f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/python/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config.ic_type, config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/python/solver.py b/example/1d-linear-convection/weno3/julia/01c/python/solver.py new file mode 100644 index 00000000..0d0b442d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/python/solver.py @@ -0,0 +1,86 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + + +from flux import InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory + +from boundary import BoundaryCondition, PeriodicBoundary, DirichletBoundary, NeumannBoundary, BoundaryConditionFactory + +from time_integration import TimeIntegrator, RK1Integrator, RK2Integrator, RK3Integrator, TimeIntegratorFactory + +from mesh import Mesh + +from reconstructor import ReconstructorFactory + +from initial_condition import InitialCondition, StepFunctionIC, SineWaveIC, GaussianPulseIC, InitialConditionFactory + +from domain import Domain +from solution import Solution + +from config import CfdConfig +from residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config.ic_type, self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + #runge_kutta(self) + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/julia/01c/python/time_integration.py b/example/1d-linear-convection/weno3/julia/01c/python/time_integration.py new file mode 100644 index 00000000..25ac0b4c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01c/python/time_integration.py @@ -0,0 +1,125 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class TimeIntegratorFactory: + """时间推进器工厂""" + + @staticmethod + def create(cfd) -> 'TimeIntegrator': + """ + 创建时间推进器实例 + + Args: + cfd: CFD对象 + + Returns: + 时间推进器实例 + """ + from factories.base_factory import BaseFactory + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01c/python/u_dirichlet_py.npy b/example/1d-linear-convection/weno3/julia/01c/python/u_dirichlet_py.npy new file mode 100644 index 00000000..3aa20bd2 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01c/python/u_dirichlet_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01c/python/u_gaussian_full_py.npy b/example/1d-linear-convection/weno3/julia/01c/python/u_gaussian_full_py.npy new file mode 100644 index 00000000..b762f0fe Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01c/python/u_gaussian_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01c/python/u_gaussian_interior_py.npy b/example/1d-linear-convection/weno3/julia/01c/python/u_gaussian_interior_py.npy new file mode 100644 index 00000000..68af8954 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01c/python/u_gaussian_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01c/python/u_input.npy b/example/1d-linear-convection/weno3/julia/01c/python/u_input.npy new file mode 100644 index 00000000..ef506fa7 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01c/python/u_input.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01c/python/u_neumann_py.npy b/example/1d-linear-convection/weno3/julia/01c/python/u_neumann_py.npy new file mode 100644 index 00000000..fa723d1e Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01c/python/u_neumann_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01c/python/u_periodic_py.npy b/example/1d-linear-convection/weno3/julia/01c/python/u_periodic_py.npy new file mode 100644 index 00000000..156aff85 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01c/python/u_periodic_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01c/python/u_sin_full_py.npy b/example/1d-linear-convection/weno3/julia/01c/python/u_sin_full_py.npy new file mode 100644 index 00000000..b87f8a13 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01c/python/u_sin_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01c/python/u_sin_interior_py.npy b/example/1d-linear-convection/weno3/julia/01c/python/u_sin_interior_py.npy new file mode 100644 index 00000000..6803fbfb Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01c/python/u_sin_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01c/python/u_step_full_py.npy b/example/1d-linear-convection/weno3/julia/01c/python/u_step_full_py.npy new file mode 100644 index 00000000..2fc0e183 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01c/python/u_step_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01c/python/u_step_interior_py.npy b/example/1d-linear-convection/weno3/julia/01c/python/u_step_interior_py.npy new file mode 100644 index 00000000..b5ad6600 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01c/python/u_step_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01d/julia/boundary.jl b/example/1d-linear-convection/weno3/julia/01d/julia/boundary.jl new file mode 100644 index 00000000..5b0baaf4 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/julia/boundary.jl @@ -0,0 +1,90 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/julia/domain.jl b/example/1d-linear-convection/weno3/julia/01d/julia/domain.jl new file mode 100644 index 00000000..818be8fa --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/julia/domain.jl @@ -0,0 +1,61 @@ +# julia/domain.jl +""" +Domain 结构体(与 domain.py 完全同构) +- 保存 config +- nghosts 计算逻辑 1:1 +- ist/ied 为 0-based(与 Python 一致) +""" + +include("mesh.jl") + +mutable struct Domain + config::Any # 保存 config(与 Python 一致) + mesh::Mesh + nghosts::Int + ist::Int # 0-based + ied::Int # 0-based + ntcells::Int +end + +function Domain(config::Any, mesh::Mesh) + nghosts = _calc_nghosts(config) + ist = nghosts + ied = ist + mesh.ncells + ntcells = mesh.ncells + 2 * nghosts + Domain(config, mesh, nghosts, ist, ied, ntcells) +end + +function _calc_nghosts(config::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + order = getfield_safe(config, :spatial_order, 2) + + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + if scheme == "eno" + nghosts = order + elseif startswith(scheme, "weno") + nghosts = order ÷ 2 + 1 + else + error("未知重建格式 $(scheme),无法计算ghost层!") + end + + if nghosts <= 0 + error("计算得到的ghost层数量无效:$(nghosts)(阶数$(order),格式$(scheme))") + end + + return nghosts +end + +function is_physical_cell(domain::Domain, idx::Int) + return domain.ist <= idx < domain.ied +end + +function get_physical_indices(domain::Domain) + return domain.ist:(domain.ied - 1) +end + +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/julia/initial_condition.jl b/example/1d-linear-convection/weno3/julia/01d/julia/initial_condition.jl new file mode 100644 index 00000000..5e029e8d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/julia/initial_condition.jl @@ -0,0 +1,86 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/julia/mesh.jl b/example/1d-linear-convection/weno3/julia/01d/julia/mesh.jl new file mode 100644 index 00000000..1b0dd4bf --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/julia/mesh.jl @@ -0,0 +1,45 @@ +# julia/mesh.jl +""" +Mesh 结构体(与提供的 mesh.py 完全同构) +- 字段名、初始化顺序、循环逻辑完全一致 +- 不做任何泛化或优化 +""" + +mutable struct Mesh + xmin::Float64 + xmax::Float64 + ncells::Int + nnodes::Int + nx::Int + x::Vector{Float64} + xcc::Vector{Float64} + L::Float64 # 在 init_mesh 中赋值 + dx::Float64 # 在 init_mesh 中赋值 + + function Mesh() + # 与 Python __init__ 完全一致 + xmin = 0.0 + xmax = 2.0 + ncells = 40 + nnodes = ncells + 1 + nx = ncells + x = zeros(Float64, nnodes) + xcc = zeros(Float64, ncells) + + # 调用 init_mesh(模拟 Python) + L = xmax - xmin + dx = L / ncells + + # Generate node coordinates: for i in range(self.nnodes) + for i in 1:nnodes + x[i] = xmin + (i - 1) * dx # Python i → Julia i-1 + end + + # Generate cell center coordinates + for i in 1:ncells + xcc[i] = 0.5 * (x[i] + x[i+1]) + end + + new(xmin, xmax, ncells, nnodes, nx, x, xcc, L, dx) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/julia/solution.jl b/example/1d-linear-convection/weno3/julia/01d/julia/solution.jl new file mode 100644 index 00000000..90ca0393 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/julia/solution.jl @@ -0,0 +1,75 @@ +# julia/solution.jl +""" +Solution 结构体(与真实 solution.py 完全同构) +字段顺序、方法、逻辑 1:1 对齐 +""" + +include("mesh.jl") +include("domain.jl") +include("initial_condition.jl") + +mutable struct Solution + domain::Domain + q_face_left::Vector{Float64} + q_face_right::Vector{Float64} + flux::Vector{Float64} + res::Vector{Float64} + u::Vector{Float64} + un::Vector{Float64} + + function Solution(config::Any, domain::Domain) + mesh = domain.mesh + + q_face_left = zeros(Float64, mesh.nnodes) + q_face_right = zeros(Float64, mesh.nnodes) + flux = zeros(Float64, mesh.nnodes) + res = zeros(Float64, mesh.ncells) + u = zeros(Float64, domain.ntcells) + un = zeros(Float64, domain.ntcells) + + sol = new(domain, q_face_left, q_face_right, flux, res, u, un) + initialize_from_config(sol, config) + return sol + end +end + +""" +重置解数组为初始状态 +""" +function reset_solution(sol::Solution) + fill!(sol.u, 0.0) + fill!(sol.un, 0.0) +end + +""" +根据配置初始化场 +注:暂用硬编码替代 InitialConditionFactory +""" +function initialize_from_config(sol::Solution, config::Any) + ic_type = getfield_safe(config, :ic_type, "step") + + # 硬编码创建 IC(替代 InitialConditionFactory) + ic = if ic_type == "step" + StepFunctionIC(config) + elseif ic_type == "sin" + SineWaveIC(config) + elseif ic_type == "gaussian" + GaussianPulseIC(config) + else + error("未知初始条件类型: $ic_type") + end + + apply(ic, sol) # 调用 IC.apply +end + +""" +更新旧场:un = u +""" +function update_old_field(sol::Solution) + sol.un .= sol.u # 等价于 Python 的 un[:] = u[:] +end + +# ---------------------- 辅助函数 ---------------------- +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/julia/test/test_boundary.jl b/example/1d-linear-convection/weno3/julia/01d/julia/test/test_boundary.jl new file mode 100644 index 00000000..ef27d682 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/julia/test/test_boundary.jl @@ -0,0 +1,57 @@ +# julia/test/test_boundary.jl +using NPZ +include("../boundary.jl") + +struct MockConfig + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + debug::Bool +end + +struct MockDomain + nghosts::Int + ist::Int # Julia 本地索引(1-based) + ied::Int + ntcells::Int +end + +struct MockCfd + config::MockConfig + domain::MockDomain +end + +# ===== 关键:使用 Julia 本地索引规则 ===== +nghosts = 2 +ncells = 40 +ist = nghosts + 1 # = 3 +ied = ist + ncells # = 43 +ntcells = ncells + 2 * nghosts # = 44 + +config = MockConfig("dirichlet", 0.5, 1.5, true) +domain = MockDomain(nghosts, ist, ied, ntcells) +cfd_mock = MockCfd(config, domain) + +# 加载 Python 生成的 u_input.npy +# 注意:Python u[0] → Julia u[1],所以内容完全对应 +u_input = npzread("../../python/u_input.npy") +@assert length(u_input) == ntcells "数组长度不匹配!" + +# 测试三种边界 +test_cases = [ + ("periodic", PeriodicBoundary(cfd_mock)), + ("dirichlet", DirichletBoundary(cfd_mock)), + ("neumann", NeumannBoundary(cfd_mock)) +] + +for (name, bc) in test_cases + u = copy(u_input) + apply!(bc, u) + + u_py = npzread("../../python/u_$(name)_py.npy") + err = maximum(abs.(u .- u_py)) + println("边界: $(name) | 最大误差: $(err)") + @assert err < 1e-12 "❌ $(name) 不一致!" +end + +println("✅ 所有边界条件测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/julia/test/test_domain.jl b/example/1d-linear-convection/weno3/julia/01d/julia/test/test_domain.jl new file mode 100644 index 00000000..7b423f77 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/julia/test/test_domain.jl @@ -0,0 +1,44 @@ +# julia/test/test_domain.jl +include("../mesh.jl") +include("../domain.jl") + +# MockConfig:模拟 Python CfdConfig +struct MockConfig + recon_scheme::String + spatial_order::Int +end + +# 测试 ENO +config_eno = MockConfig("eno", 2) +mesh = Mesh() +domain_eno = Domain(config_eno, mesh) + +println("ENO: nghosts = ", domain_eno.nghosts) # 2 +println("ENO: ist = ", domain_eno.ist) # 2 +println("ENO: ied = ", domain_eno.ied) # 42 +println("物理索引范围: ", collect(get_physical_indices(domain_eno))[1:3], " ... ", collect(get_physical_indices(domain_eno))[end-2:end]) + +# 测试 WENO(字符串 "weno") +config_weno = MockConfig("weno", 2) +domain_weno = Domain(config_weno, mesh) +println("WENO: nghosts = ", domain_weno.nghosts) # 2 + +# 测试 WENO3(字符串 "weno3") +config_weno3 = MockConfig("weno3", 2) +domain_weno3 = Domain(config_weno3, mesh) +println("WENO3: nghosts = ", domain_weno3.nghosts) # 2 + +# 测试 is_physical_cell +@assert is_physical_cell(domain_eno, 2) == true # ist=2 +@assert is_physical_cell(domain_eno, 41) == true # ied-1=41 +@assert is_physical_cell(domain_eno, 42) == false # ied=42 + +# ✅ 断言 +@assert domain_eno.nghosts == 2 +@assert domain_eno.ist == 2 +@assert domain_eno.ied == 42 +@assert domain_eno.ntcells == 44 +@assert domain_weno.nghosts == 2 +@assert domain_weno3.nghosts == 2 + +println("✅ Domain 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/julia/test/test_initial_condition.jl b/example/1d-linear-convection/weno3/julia/01d/julia/test/test_initial_condition.jl new file mode 100644 index 00000000..dc58691a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/julia/test/test_initial_condition.jl @@ -0,0 +1,45 @@ +# julia/test/test_initial_condition.jl +using NPZ + +# 包含初始条件模块 +include("../initial_condition.jl") + +# 构造 mock config(与 Python 完全一致) +struct MockConfig + ic_type::String + domain_length::Float64 + pulse_center::Float64 + pulse_width::Float64 +end + +# 生成与 Python Mesh.xcc 完全相同的 x 坐标 +function generate_xcc() + xmin, xmax = 0.0, 2.0 + ncells = 40 + dx = (xmax - xmin) / ncells + xcc = Vector{Float64}(undef, ncells) + for i in 1:ncells + xcc[i] = xmin + (i - 0.5) * dx # i-1 + 0.5 → i-0.5 + end + return xcc +end + +# 主测试 +xcc = generate_xcc() + +test_cases = [ + ("step", StepFunctionIC(MockConfig("step", 2.0, 0.5, 0.1))), + ("sin", SineWaveIC(MockConfig("sin", 2.0, 0.5, 0.1))), + ("gaussian", GaussianPulseIC(MockConfig("gaussian", 2.0, 0.5, 0.1))) +] + +for (name, ic) in test_cases + u_jl = evaluate_at(ic, xcc) + u_py = npzread("../../python/u_$(name)_interior_py.npy") + + err = maximum(abs.(u_jl .- u_py)) + println("IC: $(name) | 最大误差: $(err)") + @assert err < 1e-12 "❌ $(name) 不一致!" +end + +println("✅ 所有初始条件测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/julia/test/test_mesh.jl b/example/1d-linear-convection/weno3/julia/01d/julia/test/test_mesh.jl new file mode 100644 index 00000000..315d2aac --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/julia/test/test_mesh.jl @@ -0,0 +1,37 @@ +# julia/test/test_mesh.jl +include("../mesh.jl") + +# 创建 mesh(与 Python 完全相同) +mesh = Mesh() + +# 打印关键值(与 Python 对比) +println("xmin = ", mesh.xmin) # 0.0 +println("xmax = ", mesh.xmax) # 2.0 +println("ncells = ", mesh.ncells) # 40 +println("nnodes = ", mesh.nnodes) # 41 +println("nx = ", mesh.nx) # 40 +println("L = ", mesh.L) # 2.0 +println("dx = ", mesh.dx) # 0.05 + +# 检查 x[1] (Python x[0]) 和 x[41] (Python x[40]) +println("x[1] = ", mesh.x[1]) # 0.0 +println("x[41] = ", mesh.x[41]) # 2.0 + +# 检查 xcc[1] (Python xcc[0]) 和 xcc[40] (Python xcc[39]) +println("xcc[1] = ", mesh.xcc[1]) # 0.025 +println("xcc[40] = ", mesh.xcc[40]) # 1.975 + +# ✅ 严格断言 +@assert mesh.xmin == 0.0 +@assert mesh.xmax == 2.0 +@assert mesh.ncells == 40 +@assert mesh.nnodes == 41 +@assert mesh.nx == 40 +@assert mesh.L == 2.0 +@assert mesh.dx == 0.05 +@assert mesh.x[1] == 0.0 +@assert mesh.x[41] == 2.0 +@assert abs(mesh.xcc[1] - 0.025) < 1e-12 +@assert abs(mesh.xcc[40] - 1.975) < 1e-12 + +println("✅ Mesh 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/julia/test/test_solution.jl b/example/1d-linear-convection/weno3/julia/01d/julia/test/test_solution.jl new file mode 100644 index 00000000..f44ff0f0 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/julia/test/test_solution.jl @@ -0,0 +1,42 @@ +# julia/test/test_solution.jl +include("../mesh.jl") +include("../domain.jl") +include("../solution.jl") + +# MockConfig +struct MockConfig + recon_scheme::String + spatial_order::Int + ic_type::String + domain_length::Float64 + pulse_center::Float64 + pulse_width::Float64 +end + +# 创建 solution +config = MockConfig("eno", 2, "step", 2.0, 0.5, 0.1) +mesh = Mesh() +domain = Domain(config, mesh) +sol = Solution(config, domain) + +# 检查字段尺寸 +@assert length(sol.q_face_left) == mesh.nnodes # 41 +@assert length(sol.flux) == mesh.nnodes # 41 +@assert length(sol.res) == mesh.ncells # 40 +@assert length(sol.u) == domain.ntcells # 44 + +# 检查初始场 +println("u[3] (物理起始): ", sol.u[3]) # 应为 1.0 +println("u[23] (x=1.0): ", sol.u[23]) # 应为 2.0 + +# 测试 update_old_field +sol.u[3] = 999.0 +update_old_field(sol) +@assert sol.un[3] == 999.0 + +# 测试 reset_solution +reset_solution(sol) +@assert sol.u[3] == 0.0 +@assert sol.un[3] == 0.0 + +println("✅ Solution 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/python/boundary.py b/example/1d-linear-convection/weno3/julia/01d/python/boundary.py new file mode 100644 index 00000000..6054f92d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/python/boundary.py @@ -0,0 +1,103 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/python/cfd_registry.py b/example/1d-linear-convection/weno3/julia/01d/python/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/python/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/python/config.py b/example/1d-linear-convection/weno3/julia/01d/python/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/python/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/python/domain.py b/example/1d-linear-convection/weno3/julia/01d/python/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/python/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/python/factories/base_factory.py b/example/1d-linear-convection/weno3/julia/01d/python/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/python/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/python/flux.py b/example/1d-linear-convection/weno3/julia/01d/python/flux.py new file mode 100644 index 00000000..beb9ed48 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/python/flux.py @@ -0,0 +1,73 @@ +""" +通量计算器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册,替代硬编码工厂 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象通量计算基类(统一接口) ---------------------- +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass + +# ---------------------- 2. 具体通量计算子类(使用装饰器注册) ---------------------- + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + +class FluxCalculatorFactory: + """通量计算器工厂""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD对象 + + Returns: + 通量计算器实例 + """ + from factories.base_factory import BaseFactory + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/python/gen_boundary_test_data.py b/example/1d-linear-convection/weno3/julia/01d/python/gen_boundary_test_data.py new file mode 100644 index 00000000..c7fc2a9c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/python/gen_boundary_test_data.py @@ -0,0 +1,35 @@ +# python/gen_boundary_test_data.py +import numpy as np +from config import CfdConfig +from mesh import Mesh +from domain import Domain + +# 固定测试配置 +config = CfdConfig() +config.with_boundary("dirichlet", left_value=0.5, right_value=1.5) +config.debug = True + +mesh = Mesh() +domain = Domain(config, mesh) + +# 构造 mock CFD 对象(仅含 config + domain) +class MockCfd: + def __init__(self, config, domain): + self.config = config + self.domain = domain + +# 测试用 u:0,1,2,...,N-1 +u_input = np.arange(domain.ntcells, dtype=np.float64) +np.save("u_input.npy", u_input) + +# 测试每种边界 +from boundary import PeriodicBoundary, DirichletBoundary, NeumannBoundary + +for bc_name, bc_class in [("periodic", PeriodicBoundary), ("dirichlet", DirichletBoundary), ("neumann", NeumannBoundary)]: + u = u_input.copy() + cfd_mock = MockCfd(config, domain) + bc = bc_class(cfd_mock) + bc.apply(u) + np.save(f"u_{bc_name}_py.npy", u) + +print("✅ 测试数据已生成:u_*.npy") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/python/gen_ic_test_data.py b/example/1d-linear-convection/weno3/julia/01d/python/gen_ic_test_data.py new file mode 100644 index 00000000..69c2573b --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/python/gen_ic_test_data.py @@ -0,0 +1,27 @@ +# python/gen_ic_test_data.py +import sys, os +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +import numpy as np +from config import CfdConfig +from mesh import Mesh +from domain import Domain +from solution import Solution + +# 固定 mesh +mesh = Mesh() +config = CfdConfig() + +# 测试三种 IC +for ic_type in ["step", "sin", "gaussian"]: + config.ic_type = ic_type + domain = Domain(config, mesh) + sol = Solution(config, domain) + + u_full = sol.u.copy() # 包含 ghost + u_interior = sol.u[domain.ist:domain.ied].copy() + + np.save(f"u_{ic_type}_full_py.npy", u_full) + np.save(f"u_{ic_type}_interior_py.npy", u_interior) + +print("✅ 初始条件测试数据已生成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/python/initial_condition.py b/example/1d-linear-convection/weno3/julia/01d/python/initial_condition.py new file mode 100644 index 00000000..047415b7 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/python/initial_condition.py @@ -0,0 +1,90 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, ic_type: str, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', ic_type, config) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/python/mesh.py b/example/1d-linear-convection/weno3/julia/01d/python/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/python/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/python/plotter.py b/example/1d-linear-convection/weno3/julia/01d/python/plotter.py new file mode 100644 index 00000000..9f1a414f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/python/plotter.py @@ -0,0 +1,107 @@ +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/python/reconstructor/__init__.py b/example/1d-linear-convection/weno3/julia/01d/python/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/python/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/python/reconstructor/base.py b/example/1d-linear-convection/weno3/julia/01d/python/reconstructor/base.py new file mode 100644 index 00000000..bbd63850 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/python/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def reconstruct(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/python/reconstructor/eno.py b/example/1d-linear-convection/weno3/julia/01d/python/reconstructor/eno.py new file mode 100644 index 00000000..c2fb385d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/python/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def reconstruct(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/python/reconstructor/factory.py b/example/1d-linear-convection/weno3/julia/01d/python/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/python/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/python/reconstructor/weno3.py b/example/1d-linear-convection/weno3/julia/01d/python/reconstructor/weno3.py new file mode 100644 index 00000000..6e8c3f23 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/python/reconstructor/weno3.py @@ -0,0 +1,88 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def reconstruct(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + """ + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 # smoothness indicator for stencil [v2, v3] + beta1 = (v2 - v1)**2 # smoothness indicator for stencil [v1, v2] + + d0, d1 = 2/3, 1/3 # optimal linear weights (for right value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 0.5 * v2 + 0.5 * v3 # reconstruction from [v2, v3] + q1 = -0.5 * v1 + 1.5 * v2 # reconstruction from [v1, v2] + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 + beta1 = (v2 - v1)**2 + + d0, d1 = 1/3, 2/3 # optimal linear weights (for left value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 1.5 * v2 - 0.5 * v3 # from [v2, v3] + q1 = 0.5 * v1 + 0.5 * v2 # from [v1, v2] + return w0 * q0 + w1 * q1 + """ diff --git a/example/1d-linear-convection/weno3/julia/01d/python/registry.py b/example/1d-linear-convection/weno3/julia/01d/python/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/python/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/python/residual.py b/example/1d-linear-convection/weno3/julia/01d/python/residual.py new file mode 100644 index 00000000..b4d4d7dc --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/python/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._reconstruct() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _reconstruct(self): + """私有方法:界面值重建""" + self.reconstructor.reconstruct(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/python/run_eno_weno.py b/example/1d-linear-convection/weno3/julia/01d/python/run_eno_weno.py new file mode 100644 index 00000000..fd7f3874 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/python/run_eno_weno.py @@ -0,0 +1,52 @@ +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/python/solution.py b/example/1d-linear-convection/weno3/julia/01d/python/solution.py new file mode 100644 index 00000000..92a46d3f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/python/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config.ic_type, config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/python/solver.py b/example/1d-linear-convection/weno3/julia/01d/python/solver.py new file mode 100644 index 00000000..0d0b442d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/python/solver.py @@ -0,0 +1,86 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + + +from flux import InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory + +from boundary import BoundaryCondition, PeriodicBoundary, DirichletBoundary, NeumannBoundary, BoundaryConditionFactory + +from time_integration import TimeIntegrator, RK1Integrator, RK2Integrator, RK3Integrator, TimeIntegratorFactory + +from mesh import Mesh + +from reconstructor import ReconstructorFactory + +from initial_condition import InitialCondition, StepFunctionIC, SineWaveIC, GaussianPulseIC, InitialConditionFactory + +from domain import Domain +from solution import Solution + +from config import CfdConfig +from residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config.ic_type, self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + #runge_kutta(self) + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/julia/01d/python/time_integration.py b/example/1d-linear-convection/weno3/julia/01d/python/time_integration.py new file mode 100644 index 00000000..25ac0b4c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01d/python/time_integration.py @@ -0,0 +1,125 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class TimeIntegratorFactory: + """时间推进器工厂""" + + @staticmethod + def create(cfd) -> 'TimeIntegrator': + """ + 创建时间推进器实例 + + Args: + cfd: CFD对象 + + Returns: + 时间推进器实例 + """ + from factories.base_factory import BaseFactory + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01d/python/u_dirichlet_py.npy b/example/1d-linear-convection/weno3/julia/01d/python/u_dirichlet_py.npy new file mode 100644 index 00000000..3aa20bd2 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01d/python/u_dirichlet_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01d/python/u_gaussian_full_py.npy b/example/1d-linear-convection/weno3/julia/01d/python/u_gaussian_full_py.npy new file mode 100644 index 00000000..b762f0fe Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01d/python/u_gaussian_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01d/python/u_gaussian_interior_py.npy b/example/1d-linear-convection/weno3/julia/01d/python/u_gaussian_interior_py.npy new file mode 100644 index 00000000..68af8954 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01d/python/u_gaussian_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01d/python/u_input.npy b/example/1d-linear-convection/weno3/julia/01d/python/u_input.npy new file mode 100644 index 00000000..ef506fa7 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01d/python/u_input.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01d/python/u_neumann_py.npy b/example/1d-linear-convection/weno3/julia/01d/python/u_neumann_py.npy new file mode 100644 index 00000000..fa723d1e Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01d/python/u_neumann_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01d/python/u_periodic_py.npy b/example/1d-linear-convection/weno3/julia/01d/python/u_periodic_py.npy new file mode 100644 index 00000000..156aff85 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01d/python/u_periodic_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01d/python/u_sin_full_py.npy b/example/1d-linear-convection/weno3/julia/01d/python/u_sin_full_py.npy new file mode 100644 index 00000000..b87f8a13 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01d/python/u_sin_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01d/python/u_sin_interior_py.npy b/example/1d-linear-convection/weno3/julia/01d/python/u_sin_interior_py.npy new file mode 100644 index 00000000..6803fbfb Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01d/python/u_sin_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01d/python/u_step_full_py.npy b/example/1d-linear-convection/weno3/julia/01d/python/u_step_full_py.npy new file mode 100644 index 00000000..2fc0e183 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01d/python/u_step_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01d/python/u_step_interior_py.npy b/example/1d-linear-convection/weno3/julia/01d/python/u_step_interior_py.npy new file mode 100644 index 00000000..b5ad6600 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01d/python/u_step_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01e/julia/boundary.jl b/example/1d-linear-convection/weno3/julia/01e/julia/boundary.jl new file mode 100644 index 00000000..5b0baaf4 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/julia/boundary.jl @@ -0,0 +1,90 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/julia/domain.jl b/example/1d-linear-convection/weno3/julia/01e/julia/domain.jl new file mode 100644 index 00000000..818be8fa --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/julia/domain.jl @@ -0,0 +1,61 @@ +# julia/domain.jl +""" +Domain 结构体(与 domain.py 完全同构) +- 保存 config +- nghosts 计算逻辑 1:1 +- ist/ied 为 0-based(与 Python 一致) +""" + +include("mesh.jl") + +mutable struct Domain + config::Any # 保存 config(与 Python 一致) + mesh::Mesh + nghosts::Int + ist::Int # 0-based + ied::Int # 0-based + ntcells::Int +end + +function Domain(config::Any, mesh::Mesh) + nghosts = _calc_nghosts(config) + ist = nghosts + ied = ist + mesh.ncells + ntcells = mesh.ncells + 2 * nghosts + Domain(config, mesh, nghosts, ist, ied, ntcells) +end + +function _calc_nghosts(config::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + order = getfield_safe(config, :spatial_order, 2) + + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + if scheme == "eno" + nghosts = order + elseif startswith(scheme, "weno") + nghosts = order ÷ 2 + 1 + else + error("未知重建格式 $(scheme),无法计算ghost层!") + end + + if nghosts <= 0 + error("计算得到的ghost层数量无效:$(nghosts)(阶数$(order),格式$(scheme))") + end + + return nghosts +end + +function is_physical_cell(domain::Domain, idx::Int) + return domain.ist <= idx < domain.ied +end + +function get_physical_indices(domain::Domain) + return domain.ist:(domain.ied - 1) +end + +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/julia/flux.jl b/example/1d-linear-convection/weno3/julia/01e/julia/flux.jl new file mode 100644 index 00000000..047598f7 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/julia/flux.jl @@ -0,0 +1,70 @@ +# julia/flux.jl +""" +通量计算器模块(与 flux.py 完全同构) +- 抽象基类 + 具体实现 +- 字段:cfd, config, mesh, wave_speed +""" + +include("mesh.jl") + +# ---------------------- 抽象基类 ---------------------- +""" +InviscidFluxCalculator 抽象类型 +Julia 无 ABC,用文档约定 +所有子类型必须实现 compute! +""" +abstract type InviscidFluxCalculator end + +# ---------------------- RusanovFluxCalculator ---------------------- +mutable struct RusanovFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function RusanovFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::RusanovFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = calc.wave_speed + c_R = calc.wave_speed + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + end +end + +# ---------------------- EngquistOsherFluxCalculator ---------------------- +mutable struct EngquistOsherFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function EngquistOsherFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::EngquistOsherFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + c = calc.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/julia/initial_condition.jl b/example/1d-linear-convection/weno3/julia/01e/julia/initial_condition.jl new file mode 100644 index 00000000..5e029e8d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/julia/initial_condition.jl @@ -0,0 +1,86 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/julia/mesh.jl b/example/1d-linear-convection/weno3/julia/01e/julia/mesh.jl new file mode 100644 index 00000000..1b0dd4bf --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/julia/mesh.jl @@ -0,0 +1,45 @@ +# julia/mesh.jl +""" +Mesh 结构体(与提供的 mesh.py 完全同构) +- 字段名、初始化顺序、循环逻辑完全一致 +- 不做任何泛化或优化 +""" + +mutable struct Mesh + xmin::Float64 + xmax::Float64 + ncells::Int + nnodes::Int + nx::Int + x::Vector{Float64} + xcc::Vector{Float64} + L::Float64 # 在 init_mesh 中赋值 + dx::Float64 # 在 init_mesh 中赋值 + + function Mesh() + # 与 Python __init__ 完全一致 + xmin = 0.0 + xmax = 2.0 + ncells = 40 + nnodes = ncells + 1 + nx = ncells + x = zeros(Float64, nnodes) + xcc = zeros(Float64, ncells) + + # 调用 init_mesh(模拟 Python) + L = xmax - xmin + dx = L / ncells + + # Generate node coordinates: for i in range(self.nnodes) + for i in 1:nnodes + x[i] = xmin + (i - 1) * dx # Python i → Julia i-1 + end + + # Generate cell center coordinates + for i in 1:ncells + xcc[i] = 0.5 * (x[i] + x[i+1]) + end + + new(xmin, xmax, ncells, nnodes, nx, x, xcc, L, dx) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/julia/solution.jl b/example/1d-linear-convection/weno3/julia/01e/julia/solution.jl new file mode 100644 index 00000000..90ca0393 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/julia/solution.jl @@ -0,0 +1,75 @@ +# julia/solution.jl +""" +Solution 结构体(与真实 solution.py 完全同构) +字段顺序、方法、逻辑 1:1 对齐 +""" + +include("mesh.jl") +include("domain.jl") +include("initial_condition.jl") + +mutable struct Solution + domain::Domain + q_face_left::Vector{Float64} + q_face_right::Vector{Float64} + flux::Vector{Float64} + res::Vector{Float64} + u::Vector{Float64} + un::Vector{Float64} + + function Solution(config::Any, domain::Domain) + mesh = domain.mesh + + q_face_left = zeros(Float64, mesh.nnodes) + q_face_right = zeros(Float64, mesh.nnodes) + flux = zeros(Float64, mesh.nnodes) + res = zeros(Float64, mesh.ncells) + u = zeros(Float64, domain.ntcells) + un = zeros(Float64, domain.ntcells) + + sol = new(domain, q_face_left, q_face_right, flux, res, u, un) + initialize_from_config(sol, config) + return sol + end +end + +""" +重置解数组为初始状态 +""" +function reset_solution(sol::Solution) + fill!(sol.u, 0.0) + fill!(sol.un, 0.0) +end + +""" +根据配置初始化场 +注:暂用硬编码替代 InitialConditionFactory +""" +function initialize_from_config(sol::Solution, config::Any) + ic_type = getfield_safe(config, :ic_type, "step") + + # 硬编码创建 IC(替代 InitialConditionFactory) + ic = if ic_type == "step" + StepFunctionIC(config) + elseif ic_type == "sin" + SineWaveIC(config) + elseif ic_type == "gaussian" + GaussianPulseIC(config) + else + error("未知初始条件类型: $ic_type") + end + + apply(ic, sol) # 调用 IC.apply +end + +""" +更新旧场:un = u +""" +function update_old_field(sol::Solution) + sol.un .= sol.u # 等价于 Python 的 un[:] = u[:] +end + +# ---------------------- 辅助函数 ---------------------- +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/julia/test/test_boundary.jl b/example/1d-linear-convection/weno3/julia/01e/julia/test/test_boundary.jl new file mode 100644 index 00000000..ef27d682 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/julia/test/test_boundary.jl @@ -0,0 +1,57 @@ +# julia/test/test_boundary.jl +using NPZ +include("../boundary.jl") + +struct MockConfig + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + debug::Bool +end + +struct MockDomain + nghosts::Int + ist::Int # Julia 本地索引(1-based) + ied::Int + ntcells::Int +end + +struct MockCfd + config::MockConfig + domain::MockDomain +end + +# ===== 关键:使用 Julia 本地索引规则 ===== +nghosts = 2 +ncells = 40 +ist = nghosts + 1 # = 3 +ied = ist + ncells # = 43 +ntcells = ncells + 2 * nghosts # = 44 + +config = MockConfig("dirichlet", 0.5, 1.5, true) +domain = MockDomain(nghosts, ist, ied, ntcells) +cfd_mock = MockCfd(config, domain) + +# 加载 Python 生成的 u_input.npy +# 注意:Python u[0] → Julia u[1],所以内容完全对应 +u_input = npzread("../../python/u_input.npy") +@assert length(u_input) == ntcells "数组长度不匹配!" + +# 测试三种边界 +test_cases = [ + ("periodic", PeriodicBoundary(cfd_mock)), + ("dirichlet", DirichletBoundary(cfd_mock)), + ("neumann", NeumannBoundary(cfd_mock)) +] + +for (name, bc) in test_cases + u = copy(u_input) + apply!(bc, u) + + u_py = npzread("../../python/u_$(name)_py.npy") + err = maximum(abs.(u .- u_py)) + println("边界: $(name) | 最大误差: $(err)") + @assert err < 1e-12 "❌ $(name) 不一致!" +end + +println("✅ 所有边界条件测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/julia/test/test_domain.jl b/example/1d-linear-convection/weno3/julia/01e/julia/test/test_domain.jl new file mode 100644 index 00000000..7b423f77 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/julia/test/test_domain.jl @@ -0,0 +1,44 @@ +# julia/test/test_domain.jl +include("../mesh.jl") +include("../domain.jl") + +# MockConfig:模拟 Python CfdConfig +struct MockConfig + recon_scheme::String + spatial_order::Int +end + +# 测试 ENO +config_eno = MockConfig("eno", 2) +mesh = Mesh() +domain_eno = Domain(config_eno, mesh) + +println("ENO: nghosts = ", domain_eno.nghosts) # 2 +println("ENO: ist = ", domain_eno.ist) # 2 +println("ENO: ied = ", domain_eno.ied) # 42 +println("物理索引范围: ", collect(get_physical_indices(domain_eno))[1:3], " ... ", collect(get_physical_indices(domain_eno))[end-2:end]) + +# 测试 WENO(字符串 "weno") +config_weno = MockConfig("weno", 2) +domain_weno = Domain(config_weno, mesh) +println("WENO: nghosts = ", domain_weno.nghosts) # 2 + +# 测试 WENO3(字符串 "weno3") +config_weno3 = MockConfig("weno3", 2) +domain_weno3 = Domain(config_weno3, mesh) +println("WENO3: nghosts = ", domain_weno3.nghosts) # 2 + +# 测试 is_physical_cell +@assert is_physical_cell(domain_eno, 2) == true # ist=2 +@assert is_physical_cell(domain_eno, 41) == true # ied-1=41 +@assert is_physical_cell(domain_eno, 42) == false # ied=42 + +# ✅ 断言 +@assert domain_eno.nghosts == 2 +@assert domain_eno.ist == 2 +@assert domain_eno.ied == 42 +@assert domain_eno.ntcells == 44 +@assert domain_weno.nghosts == 2 +@assert domain_weno3.nghosts == 2 + +println("✅ Domain 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/julia/test/test_flux.jl b/example/1d-linear-convection/weno3/julia/01e/julia/test/test_flux.jl new file mode 100644 index 00000000..0ebc1dc0 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/julia/test/test_flux.jl @@ -0,0 +1,53 @@ +# julia/test/test_flux.jl +include("../mesh.jl") +include("../flux.jl") + +# Mock 对象 +struct MockConfig + flux_type::String + wave_speed::Float64 +end + +struct MockDomain + mesh::Mesh +end + +struct MockCfd + config::MockConfig + domain::MockDomain +end + +# 创建 mock +mesh = Mesh() +config = MockConfig("rusanov", 1.0) +domain = MockDomain(mesh) +cfd = MockCfd(config, domain) + +# 测试 Rusanov +rusanov = RusanovFluxCalculator(cfd) +N = mesh.nnodes +qL = [1.0, 2.0, 3.0, 4.0, 5.0, zeros(Float64, N-5)...] +qR = [2.0, 3.0, 4.0, 5.0, 6.0, zeros(Float64, N-5)...] +flux_rus = zeros(Float64, N) +compute!(rusanov, qL, qR, flux_rus) + +println("Rusanov flux[1] = ", flux_rus[1]) # 应为 1.0 +@assert abs(flux_rus[1] - 1.0) < 1e-12 + +# 测试 Engquist-Osher +eo = EngquistOsherFluxCalculator(cfd) +flux_eo = zeros(Float64, N) +compute!(eo, qL, qR, flux_eo) + +println("EO flux[1] = ", flux_eo[1]) # c=1 → cp=1, cm=0 → flux=1*1 + 0*2 = 1.0 +@assert abs(flux_eo[1] - 1.0) < 1e-12 + +# 测试 c = -1.0 +config_neg = MockConfig("rusanov", -1.0) +cfd_neg = MockCfd(config_neg, domain) +rusanov_neg = RusanovFluxCalculator(cfd_neg) +compute!(rusanov_neg, qL, qR, flux_rus) +println("Rusanov (c=-1) flux[1] = ", flux_rus[1]) # F_L=-1, F_R=-2, Smax=1 → flux = -1.5 -0.5*(-1) = -1.0 +@assert abs(flux_rus[1] - (-2.0)) < 1e-12 # ← 修正:-2.0 而非 -1.0 + +println("✅ Flux 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/julia/test/test_initial_condition.jl b/example/1d-linear-convection/weno3/julia/01e/julia/test/test_initial_condition.jl new file mode 100644 index 00000000..dc58691a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/julia/test/test_initial_condition.jl @@ -0,0 +1,45 @@ +# julia/test/test_initial_condition.jl +using NPZ + +# 包含初始条件模块 +include("../initial_condition.jl") + +# 构造 mock config(与 Python 完全一致) +struct MockConfig + ic_type::String + domain_length::Float64 + pulse_center::Float64 + pulse_width::Float64 +end + +# 生成与 Python Mesh.xcc 完全相同的 x 坐标 +function generate_xcc() + xmin, xmax = 0.0, 2.0 + ncells = 40 + dx = (xmax - xmin) / ncells + xcc = Vector{Float64}(undef, ncells) + for i in 1:ncells + xcc[i] = xmin + (i - 0.5) * dx # i-1 + 0.5 → i-0.5 + end + return xcc +end + +# 主测试 +xcc = generate_xcc() + +test_cases = [ + ("step", StepFunctionIC(MockConfig("step", 2.0, 0.5, 0.1))), + ("sin", SineWaveIC(MockConfig("sin", 2.0, 0.5, 0.1))), + ("gaussian", GaussianPulseIC(MockConfig("gaussian", 2.0, 0.5, 0.1))) +] + +for (name, ic) in test_cases + u_jl = evaluate_at(ic, xcc) + u_py = npzread("../../python/u_$(name)_interior_py.npy") + + err = maximum(abs.(u_jl .- u_py)) + println("IC: $(name) | 最大误差: $(err)") + @assert err < 1e-12 "❌ $(name) 不一致!" +end + +println("✅ 所有初始条件测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/julia/test/test_mesh.jl b/example/1d-linear-convection/weno3/julia/01e/julia/test/test_mesh.jl new file mode 100644 index 00000000..315d2aac --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/julia/test/test_mesh.jl @@ -0,0 +1,37 @@ +# julia/test/test_mesh.jl +include("../mesh.jl") + +# 创建 mesh(与 Python 完全相同) +mesh = Mesh() + +# 打印关键值(与 Python 对比) +println("xmin = ", mesh.xmin) # 0.0 +println("xmax = ", mesh.xmax) # 2.0 +println("ncells = ", mesh.ncells) # 40 +println("nnodes = ", mesh.nnodes) # 41 +println("nx = ", mesh.nx) # 40 +println("L = ", mesh.L) # 2.0 +println("dx = ", mesh.dx) # 0.05 + +# 检查 x[1] (Python x[0]) 和 x[41] (Python x[40]) +println("x[1] = ", mesh.x[1]) # 0.0 +println("x[41] = ", mesh.x[41]) # 2.0 + +# 检查 xcc[1] (Python xcc[0]) 和 xcc[40] (Python xcc[39]) +println("xcc[1] = ", mesh.xcc[1]) # 0.025 +println("xcc[40] = ", mesh.xcc[40]) # 1.975 + +# ✅ 严格断言 +@assert mesh.xmin == 0.0 +@assert mesh.xmax == 2.0 +@assert mesh.ncells == 40 +@assert mesh.nnodes == 41 +@assert mesh.nx == 40 +@assert mesh.L == 2.0 +@assert mesh.dx == 0.05 +@assert mesh.x[1] == 0.0 +@assert mesh.x[41] == 2.0 +@assert abs(mesh.xcc[1] - 0.025) < 1e-12 +@assert abs(mesh.xcc[40] - 1.975) < 1e-12 + +println("✅ Mesh 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/julia/test/test_solution.jl b/example/1d-linear-convection/weno3/julia/01e/julia/test/test_solution.jl new file mode 100644 index 00000000..f44ff0f0 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/julia/test/test_solution.jl @@ -0,0 +1,42 @@ +# julia/test/test_solution.jl +include("../mesh.jl") +include("../domain.jl") +include("../solution.jl") + +# MockConfig +struct MockConfig + recon_scheme::String + spatial_order::Int + ic_type::String + domain_length::Float64 + pulse_center::Float64 + pulse_width::Float64 +end + +# 创建 solution +config = MockConfig("eno", 2, "step", 2.0, 0.5, 0.1) +mesh = Mesh() +domain = Domain(config, mesh) +sol = Solution(config, domain) + +# 检查字段尺寸 +@assert length(sol.q_face_left) == mesh.nnodes # 41 +@assert length(sol.flux) == mesh.nnodes # 41 +@assert length(sol.res) == mesh.ncells # 40 +@assert length(sol.u) == domain.ntcells # 44 + +# 检查初始场 +println("u[3] (物理起始): ", sol.u[3]) # 应为 1.0 +println("u[23] (x=1.0): ", sol.u[23]) # 应为 2.0 + +# 测试 update_old_field +sol.u[3] = 999.0 +update_old_field(sol) +@assert sol.un[3] == 999.0 + +# 测试 reset_solution +reset_solution(sol) +@assert sol.u[3] == 0.0 +@assert sol.un[3] == 0.0 + +println("✅ Solution 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/python/boundary.py b/example/1d-linear-convection/weno3/julia/01e/python/boundary.py new file mode 100644 index 00000000..6054f92d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/python/boundary.py @@ -0,0 +1,103 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/python/cfd_registry.py b/example/1d-linear-convection/weno3/julia/01e/python/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/python/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/python/config.py b/example/1d-linear-convection/weno3/julia/01e/python/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/python/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/python/domain.py b/example/1d-linear-convection/weno3/julia/01e/python/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/python/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/python/factories/base_factory.py b/example/1d-linear-convection/weno3/julia/01e/python/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/python/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/python/flux.py b/example/1d-linear-convection/weno3/julia/01e/python/flux.py new file mode 100644 index 00000000..5ac73aa8 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/python/flux.py @@ -0,0 +1,74 @@ +# flux.py +""" +通量计算器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册,替代硬编码工厂 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象通量计算基类(统一接口) ---------------------- +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass + +# ---------------------- 2. 具体通量计算子类(使用装饰器注册) ---------------------- + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + +class FluxCalculatorFactory: + """通量计算器工厂""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD对象 + + Returns: + 通量计算器实例 + """ + from factories.base_factory import BaseFactory + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/python/gen_boundary_test_data.py b/example/1d-linear-convection/weno3/julia/01e/python/gen_boundary_test_data.py new file mode 100644 index 00000000..c7fc2a9c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/python/gen_boundary_test_data.py @@ -0,0 +1,35 @@ +# python/gen_boundary_test_data.py +import numpy as np +from config import CfdConfig +from mesh import Mesh +from domain import Domain + +# 固定测试配置 +config = CfdConfig() +config.with_boundary("dirichlet", left_value=0.5, right_value=1.5) +config.debug = True + +mesh = Mesh() +domain = Domain(config, mesh) + +# 构造 mock CFD 对象(仅含 config + domain) +class MockCfd: + def __init__(self, config, domain): + self.config = config + self.domain = domain + +# 测试用 u:0,1,2,...,N-1 +u_input = np.arange(domain.ntcells, dtype=np.float64) +np.save("u_input.npy", u_input) + +# 测试每种边界 +from boundary import PeriodicBoundary, DirichletBoundary, NeumannBoundary + +for bc_name, bc_class in [("periodic", PeriodicBoundary), ("dirichlet", DirichletBoundary), ("neumann", NeumannBoundary)]: + u = u_input.copy() + cfd_mock = MockCfd(config, domain) + bc = bc_class(cfd_mock) + bc.apply(u) + np.save(f"u_{bc_name}_py.npy", u) + +print("✅ 测试数据已生成:u_*.npy") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/python/gen_ic_test_data.py b/example/1d-linear-convection/weno3/julia/01e/python/gen_ic_test_data.py new file mode 100644 index 00000000..69c2573b --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/python/gen_ic_test_data.py @@ -0,0 +1,27 @@ +# python/gen_ic_test_data.py +import sys, os +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +import numpy as np +from config import CfdConfig +from mesh import Mesh +from domain import Domain +from solution import Solution + +# 固定 mesh +mesh = Mesh() +config = CfdConfig() + +# 测试三种 IC +for ic_type in ["step", "sin", "gaussian"]: + config.ic_type = ic_type + domain = Domain(config, mesh) + sol = Solution(config, domain) + + u_full = sol.u.copy() # 包含 ghost + u_interior = sol.u[domain.ist:domain.ied].copy() + + np.save(f"u_{ic_type}_full_py.npy", u_full) + np.save(f"u_{ic_type}_interior_py.npy", u_interior) + +print("✅ 初始条件测试数据已生成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/python/initial_condition.py b/example/1d-linear-convection/weno3/julia/01e/python/initial_condition.py new file mode 100644 index 00000000..047415b7 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/python/initial_condition.py @@ -0,0 +1,90 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, ic_type: str, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', ic_type, config) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/python/mesh.py b/example/1d-linear-convection/weno3/julia/01e/python/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/python/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/python/plotter.py b/example/1d-linear-convection/weno3/julia/01e/python/plotter.py new file mode 100644 index 00000000..9f1a414f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/python/plotter.py @@ -0,0 +1,107 @@ +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/python/reconstructor/__init__.py b/example/1d-linear-convection/weno3/julia/01e/python/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/python/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/python/reconstructor/base.py b/example/1d-linear-convection/weno3/julia/01e/python/reconstructor/base.py new file mode 100644 index 00000000..bbd63850 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/python/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def reconstruct(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/python/reconstructor/eno.py b/example/1d-linear-convection/weno3/julia/01e/python/reconstructor/eno.py new file mode 100644 index 00000000..c2fb385d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/python/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def reconstruct(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/python/reconstructor/factory.py b/example/1d-linear-convection/weno3/julia/01e/python/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/python/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/python/reconstructor/weno3.py b/example/1d-linear-convection/weno3/julia/01e/python/reconstructor/weno3.py new file mode 100644 index 00000000..6e8c3f23 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/python/reconstructor/weno3.py @@ -0,0 +1,88 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def reconstruct(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + """ + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 # smoothness indicator for stencil [v2, v3] + beta1 = (v2 - v1)**2 # smoothness indicator for stencil [v1, v2] + + d0, d1 = 2/3, 1/3 # optimal linear weights (for right value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 0.5 * v2 + 0.5 * v3 # reconstruction from [v2, v3] + q1 = -0.5 * v1 + 1.5 * v2 # reconstruction from [v1, v2] + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 + beta1 = (v2 - v1)**2 + + d0, d1 = 1/3, 2/3 # optimal linear weights (for left value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 1.5 * v2 - 0.5 * v3 # from [v2, v3] + q1 = 0.5 * v1 + 0.5 * v2 # from [v1, v2] + return w0 * q0 + w1 * q1 + """ diff --git a/example/1d-linear-convection/weno3/julia/01e/python/registry.py b/example/1d-linear-convection/weno3/julia/01e/python/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/python/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/python/residual.py b/example/1d-linear-convection/weno3/julia/01e/python/residual.py new file mode 100644 index 00000000..b4d4d7dc --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/python/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._reconstruct() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _reconstruct(self): + """私有方法:界面值重建""" + self.reconstructor.reconstruct(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/python/run_eno_weno.py b/example/1d-linear-convection/weno3/julia/01e/python/run_eno_weno.py new file mode 100644 index 00000000..fd7f3874 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/python/run_eno_weno.py @@ -0,0 +1,52 @@ +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/python/solution.py b/example/1d-linear-convection/weno3/julia/01e/python/solution.py new file mode 100644 index 00000000..92a46d3f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/python/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config.ic_type, config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/python/solver.py b/example/1d-linear-convection/weno3/julia/01e/python/solver.py new file mode 100644 index 00000000..0d0b442d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/python/solver.py @@ -0,0 +1,86 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + + +from flux import InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory + +from boundary import BoundaryCondition, PeriodicBoundary, DirichletBoundary, NeumannBoundary, BoundaryConditionFactory + +from time_integration import TimeIntegrator, RK1Integrator, RK2Integrator, RK3Integrator, TimeIntegratorFactory + +from mesh import Mesh + +from reconstructor import ReconstructorFactory + +from initial_condition import InitialCondition, StepFunctionIC, SineWaveIC, GaussianPulseIC, InitialConditionFactory + +from domain import Domain +from solution import Solution + +from config import CfdConfig +from residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config.ic_type, self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + #runge_kutta(self) + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/julia/01e/python/time_integration.py b/example/1d-linear-convection/weno3/julia/01e/python/time_integration.py new file mode 100644 index 00000000..25ac0b4c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01e/python/time_integration.py @@ -0,0 +1,125 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class TimeIntegratorFactory: + """时间推进器工厂""" + + @staticmethod + def create(cfd) -> 'TimeIntegrator': + """ + 创建时间推进器实例 + + Args: + cfd: CFD对象 + + Returns: + 时间推进器实例 + """ + from factories.base_factory import BaseFactory + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01e/python/u_dirichlet_py.npy b/example/1d-linear-convection/weno3/julia/01e/python/u_dirichlet_py.npy new file mode 100644 index 00000000..3aa20bd2 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01e/python/u_dirichlet_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01e/python/u_gaussian_full_py.npy b/example/1d-linear-convection/weno3/julia/01e/python/u_gaussian_full_py.npy new file mode 100644 index 00000000..b762f0fe Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01e/python/u_gaussian_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01e/python/u_gaussian_interior_py.npy b/example/1d-linear-convection/weno3/julia/01e/python/u_gaussian_interior_py.npy new file mode 100644 index 00000000..68af8954 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01e/python/u_gaussian_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01e/python/u_input.npy b/example/1d-linear-convection/weno3/julia/01e/python/u_input.npy new file mode 100644 index 00000000..ef506fa7 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01e/python/u_input.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01e/python/u_neumann_py.npy b/example/1d-linear-convection/weno3/julia/01e/python/u_neumann_py.npy new file mode 100644 index 00000000..fa723d1e Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01e/python/u_neumann_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01e/python/u_periodic_py.npy b/example/1d-linear-convection/weno3/julia/01e/python/u_periodic_py.npy new file mode 100644 index 00000000..156aff85 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01e/python/u_periodic_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01e/python/u_sin_full_py.npy b/example/1d-linear-convection/weno3/julia/01e/python/u_sin_full_py.npy new file mode 100644 index 00000000..b87f8a13 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01e/python/u_sin_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01e/python/u_sin_interior_py.npy b/example/1d-linear-convection/weno3/julia/01e/python/u_sin_interior_py.npy new file mode 100644 index 00000000..6803fbfb Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01e/python/u_sin_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01e/python/u_step_full_py.npy b/example/1d-linear-convection/weno3/julia/01e/python/u_step_full_py.npy new file mode 100644 index 00000000..2fc0e183 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01e/python/u_step_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01e/python/u_step_interior_py.npy b/example/1d-linear-convection/weno3/julia/01e/python/u_step_interior_py.npy new file mode 100644 index 00000000..b5ad6600 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01e/python/u_step_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01f/julia/boundary.jl b/example/1d-linear-convection/weno3/julia/01f/julia/boundary.jl new file mode 100644 index 00000000..5b0baaf4 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/julia/boundary.jl @@ -0,0 +1,90 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/julia/domain.jl b/example/1d-linear-convection/weno3/julia/01f/julia/domain.jl new file mode 100644 index 00000000..818be8fa --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/julia/domain.jl @@ -0,0 +1,61 @@ +# julia/domain.jl +""" +Domain 结构体(与 domain.py 完全同构) +- 保存 config +- nghosts 计算逻辑 1:1 +- ist/ied 为 0-based(与 Python 一致) +""" + +include("mesh.jl") + +mutable struct Domain + config::Any # 保存 config(与 Python 一致) + mesh::Mesh + nghosts::Int + ist::Int # 0-based + ied::Int # 0-based + ntcells::Int +end + +function Domain(config::Any, mesh::Mesh) + nghosts = _calc_nghosts(config) + ist = nghosts + ied = ist + mesh.ncells + ntcells = mesh.ncells + 2 * nghosts + Domain(config, mesh, nghosts, ist, ied, ntcells) +end + +function _calc_nghosts(config::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + order = getfield_safe(config, :spatial_order, 2) + + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + if scheme == "eno" + nghosts = order + elseif startswith(scheme, "weno") + nghosts = order ÷ 2 + 1 + else + error("未知重建格式 $(scheme),无法计算ghost层!") + end + + if nghosts <= 0 + error("计算得到的ghost层数量无效:$(nghosts)(阶数$(order),格式$(scheme))") + end + + return nghosts +end + +function is_physical_cell(domain::Domain, idx::Int) + return domain.ist <= idx < domain.ied +end + +function get_physical_indices(domain::Domain) + return domain.ist:(domain.ied - 1) +end + +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/julia/flux.jl b/example/1d-linear-convection/weno3/julia/01f/julia/flux.jl new file mode 100644 index 00000000..047598f7 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/julia/flux.jl @@ -0,0 +1,70 @@ +# julia/flux.jl +""" +通量计算器模块(与 flux.py 完全同构) +- 抽象基类 + 具体实现 +- 字段:cfd, config, mesh, wave_speed +""" + +include("mesh.jl") + +# ---------------------- 抽象基类 ---------------------- +""" +InviscidFluxCalculator 抽象类型 +Julia 无 ABC,用文档约定 +所有子类型必须实现 compute! +""" +abstract type InviscidFluxCalculator end + +# ---------------------- RusanovFluxCalculator ---------------------- +mutable struct RusanovFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function RusanovFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::RusanovFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = calc.wave_speed + c_R = calc.wave_speed + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + end +end + +# ---------------------- EngquistOsherFluxCalculator ---------------------- +mutable struct EngquistOsherFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function EngquistOsherFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::EngquistOsherFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + c = calc.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/julia/initial_condition.jl b/example/1d-linear-convection/weno3/julia/01f/julia/initial_condition.jl new file mode 100644 index 00000000..5e029e8d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/julia/initial_condition.jl @@ -0,0 +1,86 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/julia/mesh.jl b/example/1d-linear-convection/weno3/julia/01f/julia/mesh.jl new file mode 100644 index 00000000..1b0dd4bf --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/julia/mesh.jl @@ -0,0 +1,45 @@ +# julia/mesh.jl +""" +Mesh 结构体(与提供的 mesh.py 完全同构) +- 字段名、初始化顺序、循环逻辑完全一致 +- 不做任何泛化或优化 +""" + +mutable struct Mesh + xmin::Float64 + xmax::Float64 + ncells::Int + nnodes::Int + nx::Int + x::Vector{Float64} + xcc::Vector{Float64} + L::Float64 # 在 init_mesh 中赋值 + dx::Float64 # 在 init_mesh 中赋值 + + function Mesh() + # 与 Python __init__ 完全一致 + xmin = 0.0 + xmax = 2.0 + ncells = 40 + nnodes = ncells + 1 + nx = ncells + x = zeros(Float64, nnodes) + xcc = zeros(Float64, ncells) + + # 调用 init_mesh(模拟 Python) + L = xmax - xmin + dx = L / ncells + + # Generate node coordinates: for i in range(self.nnodes) + for i in 1:nnodes + x[i] = xmin + (i - 1) * dx # Python i → Julia i-1 + end + + # Generate cell center coordinates + for i in 1:ncells + xcc[i] = 0.5 * (x[i] + x[i+1]) + end + + new(xmin, xmax, ncells, nnodes, nx, x, xcc, L, dx) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/julia/residual.jl b/example/1d-linear-convection/weno3/julia/01f/julia/residual.jl new file mode 100644 index 00000000..15cc9387 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/julia/residual.jl @@ -0,0 +1,65 @@ +# julia/residual.jl +""" +残差计算器(与 residual.py 完全同构) +- 封装重建→通量→散度完整流程 +- 依赖 cfd 的多个字段 +""" + +include("mesh.jl") + +mutable struct ResidualCalculator + cfd::Any + config::Any + domain::Any + solution::Any + mesh::Mesh + reconstructor::Any + flux_calculator::Any # 通量计算器(外部传入,替代工厂) + + function ResidualCalculator(cfd::Any, flux_calculator::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + mesh = domain.mesh + reconstructor = cfd.reconstructor + + new(cfd, config, domain, solution, mesh, reconstructor, flux_calculator) + end +end + +""" +计算完整残差(对外唯一接口) +""" +function compute!(calc::ResidualCalculator) + #_reconstruct(calc) + _compute_inviscid_flux(calc) + _compute_flux_divergence(calc) +end + +""" +私有方法:界面值重建 +""" +function _reconstruct(calc::ResidualCalculator) + reconstruct(calc.reconstructor, calc.solution.u, calc.cfd) +end + +""" +私有方法:计算无粘通量 +""" +function _compute_inviscid_flux(calc::ResidualCalculator) + compute!(calc.flux_calculator, + calc.solution.q_face_left, + calc.solution.q_face_right, + calc.solution.flux) +end + +""" +私有方法:计算通量散度(残差 = -dF/dx) +""" +function _compute_flux_divergence(calc::ResidualCalculator) + solution = calc.solution + mesh = calc.mesh + for i in 1:mesh.ncells + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / mesh.dx + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/julia/solution.jl b/example/1d-linear-convection/weno3/julia/01f/julia/solution.jl new file mode 100644 index 00000000..90ca0393 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/julia/solution.jl @@ -0,0 +1,75 @@ +# julia/solution.jl +""" +Solution 结构体(与真实 solution.py 完全同构) +字段顺序、方法、逻辑 1:1 对齐 +""" + +include("mesh.jl") +include("domain.jl") +include("initial_condition.jl") + +mutable struct Solution + domain::Domain + q_face_left::Vector{Float64} + q_face_right::Vector{Float64} + flux::Vector{Float64} + res::Vector{Float64} + u::Vector{Float64} + un::Vector{Float64} + + function Solution(config::Any, domain::Domain) + mesh = domain.mesh + + q_face_left = zeros(Float64, mesh.nnodes) + q_face_right = zeros(Float64, mesh.nnodes) + flux = zeros(Float64, mesh.nnodes) + res = zeros(Float64, mesh.ncells) + u = zeros(Float64, domain.ntcells) + un = zeros(Float64, domain.ntcells) + + sol = new(domain, q_face_left, q_face_right, flux, res, u, un) + initialize_from_config(sol, config) + return sol + end +end + +""" +重置解数组为初始状态 +""" +function reset_solution(sol::Solution) + fill!(sol.u, 0.0) + fill!(sol.un, 0.0) +end + +""" +根据配置初始化场 +注:暂用硬编码替代 InitialConditionFactory +""" +function initialize_from_config(sol::Solution, config::Any) + ic_type = getfield_safe(config, :ic_type, "step") + + # 硬编码创建 IC(替代 InitialConditionFactory) + ic = if ic_type == "step" + StepFunctionIC(config) + elseif ic_type == "sin" + SineWaveIC(config) + elseif ic_type == "gaussian" + GaussianPulseIC(config) + else + error("未知初始条件类型: $ic_type") + end + + apply(ic, sol) # 调用 IC.apply +end + +""" +更新旧场:un = u +""" +function update_old_field(sol::Solution) + sol.un .= sol.u # 等价于 Python 的 un[:] = u[:] +end + +# ---------------------- 辅助函数 ---------------------- +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/julia/test/test_boundary.jl b/example/1d-linear-convection/weno3/julia/01f/julia/test/test_boundary.jl new file mode 100644 index 00000000..ef27d682 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/julia/test/test_boundary.jl @@ -0,0 +1,57 @@ +# julia/test/test_boundary.jl +using NPZ +include("../boundary.jl") + +struct MockConfig + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + debug::Bool +end + +struct MockDomain + nghosts::Int + ist::Int # Julia 本地索引(1-based) + ied::Int + ntcells::Int +end + +struct MockCfd + config::MockConfig + domain::MockDomain +end + +# ===== 关键:使用 Julia 本地索引规则 ===== +nghosts = 2 +ncells = 40 +ist = nghosts + 1 # = 3 +ied = ist + ncells # = 43 +ntcells = ncells + 2 * nghosts # = 44 + +config = MockConfig("dirichlet", 0.5, 1.5, true) +domain = MockDomain(nghosts, ist, ied, ntcells) +cfd_mock = MockCfd(config, domain) + +# 加载 Python 生成的 u_input.npy +# 注意:Python u[0] → Julia u[1],所以内容完全对应 +u_input = npzread("../../python/u_input.npy") +@assert length(u_input) == ntcells "数组长度不匹配!" + +# 测试三种边界 +test_cases = [ + ("periodic", PeriodicBoundary(cfd_mock)), + ("dirichlet", DirichletBoundary(cfd_mock)), + ("neumann", NeumannBoundary(cfd_mock)) +] + +for (name, bc) in test_cases + u = copy(u_input) + apply!(bc, u) + + u_py = npzread("../../python/u_$(name)_py.npy") + err = maximum(abs.(u .- u_py)) + println("边界: $(name) | 最大误差: $(err)") + @assert err < 1e-12 "❌ $(name) 不一致!" +end + +println("✅ 所有边界条件测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/julia/test/test_domain.jl b/example/1d-linear-convection/weno3/julia/01f/julia/test/test_domain.jl new file mode 100644 index 00000000..7b423f77 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/julia/test/test_domain.jl @@ -0,0 +1,44 @@ +# julia/test/test_domain.jl +include("../mesh.jl") +include("../domain.jl") + +# MockConfig:模拟 Python CfdConfig +struct MockConfig + recon_scheme::String + spatial_order::Int +end + +# 测试 ENO +config_eno = MockConfig("eno", 2) +mesh = Mesh() +domain_eno = Domain(config_eno, mesh) + +println("ENO: nghosts = ", domain_eno.nghosts) # 2 +println("ENO: ist = ", domain_eno.ist) # 2 +println("ENO: ied = ", domain_eno.ied) # 42 +println("物理索引范围: ", collect(get_physical_indices(domain_eno))[1:3], " ... ", collect(get_physical_indices(domain_eno))[end-2:end]) + +# 测试 WENO(字符串 "weno") +config_weno = MockConfig("weno", 2) +domain_weno = Domain(config_weno, mesh) +println("WENO: nghosts = ", domain_weno.nghosts) # 2 + +# 测试 WENO3(字符串 "weno3") +config_weno3 = MockConfig("weno3", 2) +domain_weno3 = Domain(config_weno3, mesh) +println("WENO3: nghosts = ", domain_weno3.nghosts) # 2 + +# 测试 is_physical_cell +@assert is_physical_cell(domain_eno, 2) == true # ist=2 +@assert is_physical_cell(domain_eno, 41) == true # ied-1=41 +@assert is_physical_cell(domain_eno, 42) == false # ied=42 + +# ✅ 断言 +@assert domain_eno.nghosts == 2 +@assert domain_eno.ist == 2 +@assert domain_eno.ied == 42 +@assert domain_eno.ntcells == 44 +@assert domain_weno.nghosts == 2 +@assert domain_weno3.nghosts == 2 + +println("✅ Domain 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/julia/test/test_flux.jl b/example/1d-linear-convection/weno3/julia/01f/julia/test/test_flux.jl new file mode 100644 index 00000000..0ebc1dc0 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/julia/test/test_flux.jl @@ -0,0 +1,53 @@ +# julia/test/test_flux.jl +include("../mesh.jl") +include("../flux.jl") + +# Mock 对象 +struct MockConfig + flux_type::String + wave_speed::Float64 +end + +struct MockDomain + mesh::Mesh +end + +struct MockCfd + config::MockConfig + domain::MockDomain +end + +# 创建 mock +mesh = Mesh() +config = MockConfig("rusanov", 1.0) +domain = MockDomain(mesh) +cfd = MockCfd(config, domain) + +# 测试 Rusanov +rusanov = RusanovFluxCalculator(cfd) +N = mesh.nnodes +qL = [1.0, 2.0, 3.0, 4.0, 5.0, zeros(Float64, N-5)...] +qR = [2.0, 3.0, 4.0, 5.0, 6.0, zeros(Float64, N-5)...] +flux_rus = zeros(Float64, N) +compute!(rusanov, qL, qR, flux_rus) + +println("Rusanov flux[1] = ", flux_rus[1]) # 应为 1.0 +@assert abs(flux_rus[1] - 1.0) < 1e-12 + +# 测试 Engquist-Osher +eo = EngquistOsherFluxCalculator(cfd) +flux_eo = zeros(Float64, N) +compute!(eo, qL, qR, flux_eo) + +println("EO flux[1] = ", flux_eo[1]) # c=1 → cp=1, cm=0 → flux=1*1 + 0*2 = 1.0 +@assert abs(flux_eo[1] - 1.0) < 1e-12 + +# 测试 c = -1.0 +config_neg = MockConfig("rusanov", -1.0) +cfd_neg = MockCfd(config_neg, domain) +rusanov_neg = RusanovFluxCalculator(cfd_neg) +compute!(rusanov_neg, qL, qR, flux_rus) +println("Rusanov (c=-1) flux[1] = ", flux_rus[1]) # F_L=-1, F_R=-2, Smax=1 → flux = -1.5 -0.5*(-1) = -1.0 +@assert abs(flux_rus[1] - (-2.0)) < 1e-12 # ← 修正:-2.0 而非 -1.0 + +println("✅ Flux 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/julia/test/test_initial_condition.jl b/example/1d-linear-convection/weno3/julia/01f/julia/test/test_initial_condition.jl new file mode 100644 index 00000000..dc58691a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/julia/test/test_initial_condition.jl @@ -0,0 +1,45 @@ +# julia/test/test_initial_condition.jl +using NPZ + +# 包含初始条件模块 +include("../initial_condition.jl") + +# 构造 mock config(与 Python 完全一致) +struct MockConfig + ic_type::String + domain_length::Float64 + pulse_center::Float64 + pulse_width::Float64 +end + +# 生成与 Python Mesh.xcc 完全相同的 x 坐标 +function generate_xcc() + xmin, xmax = 0.0, 2.0 + ncells = 40 + dx = (xmax - xmin) / ncells + xcc = Vector{Float64}(undef, ncells) + for i in 1:ncells + xcc[i] = xmin + (i - 0.5) * dx # i-1 + 0.5 → i-0.5 + end + return xcc +end + +# 主测试 +xcc = generate_xcc() + +test_cases = [ + ("step", StepFunctionIC(MockConfig("step", 2.0, 0.5, 0.1))), + ("sin", SineWaveIC(MockConfig("sin", 2.0, 0.5, 0.1))), + ("gaussian", GaussianPulseIC(MockConfig("gaussian", 2.0, 0.5, 0.1))) +] + +for (name, ic) in test_cases + u_jl = evaluate_at(ic, xcc) + u_py = npzread("../../python/u_$(name)_interior_py.npy") + + err = maximum(abs.(u_jl .- u_py)) + println("IC: $(name) | 最大误差: $(err)") + @assert err < 1e-12 "❌ $(name) 不一致!" +end + +println("✅ 所有初始条件测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/julia/test/test_mesh.jl b/example/1d-linear-convection/weno3/julia/01f/julia/test/test_mesh.jl new file mode 100644 index 00000000..315d2aac --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/julia/test/test_mesh.jl @@ -0,0 +1,37 @@ +# julia/test/test_mesh.jl +include("../mesh.jl") + +# 创建 mesh(与 Python 完全相同) +mesh = Mesh() + +# 打印关键值(与 Python 对比) +println("xmin = ", mesh.xmin) # 0.0 +println("xmax = ", mesh.xmax) # 2.0 +println("ncells = ", mesh.ncells) # 40 +println("nnodes = ", mesh.nnodes) # 41 +println("nx = ", mesh.nx) # 40 +println("L = ", mesh.L) # 2.0 +println("dx = ", mesh.dx) # 0.05 + +# 检查 x[1] (Python x[0]) 和 x[41] (Python x[40]) +println("x[1] = ", mesh.x[1]) # 0.0 +println("x[41] = ", mesh.x[41]) # 2.0 + +# 检查 xcc[1] (Python xcc[0]) 和 xcc[40] (Python xcc[39]) +println("xcc[1] = ", mesh.xcc[1]) # 0.025 +println("xcc[40] = ", mesh.xcc[40]) # 1.975 + +# ✅ 严格断言 +@assert mesh.xmin == 0.0 +@assert mesh.xmax == 2.0 +@assert mesh.ncells == 40 +@assert mesh.nnodes == 41 +@assert mesh.nx == 40 +@assert mesh.L == 2.0 +@assert mesh.dx == 0.05 +@assert mesh.x[1] == 0.0 +@assert mesh.x[41] == 2.0 +@assert abs(mesh.xcc[1] - 0.025) < 1e-12 +@assert abs(mesh.xcc[40] - 1.975) < 1e-12 + +println("✅ Mesh 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/julia/test/test_residual.jl b/example/1d-linear-convection/weno3/julia/01f/julia/test/test_residual.jl new file mode 100644 index 00000000..e37e8d2b --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/julia/test/test_residual.jl @@ -0,0 +1,53 @@ +# julia/test/test_residual.jl +include("../mesh.jl") +include("../domain.jl") +include("../solution.jl") +include("../flux.jl") +include("../residual.jl") + +# ===== Dummy Reconstructor ===== +struct DummyReconstructor end + +# ===== Mock Config ===== +struct MockConfig + flux_type::String + wave_speed::Float64 +end + +# ===== Mock CFD (必须包含 reconstructor 字段) ===== +struct MockCfd + config::MockConfig + domain::Domain + solution::Solution + reconstructor::DummyReconstructor # ← 关键:添加此字段 +end + +# ===== 主测试 ===== +config = MockConfig("rusanov", 1.0) +mesh = Mesh() +domain = Domain((recon_scheme="eno", spatial_order=2), mesh) +solution = Solution((ic_type="step",), domain) + +# 手动设置界面值(跳过重建) +for i in 1:mesh.nnodes + solution.q_face_left[i] = Float64(i) * 0.1 + solution.q_face_right[i] = Float64(i) * 0.1 +end + +flux_calc = RusanovFluxCalculator((config=config, domain=domain)) +dummy_recon = DummyReconstructor() +cfd = MockCfd(config, domain, solution, dummy_recon) + +# 创建残差计算器 +res_calc = ResidualCalculator(cfd, flux_calc) + +# 计算残差(注意:_reconstruct 仍被注释) +compute!(res_calc) + +println("flux[1] = ", solution.flux[1]) +println("res[1] = ", solution.res[1]) + +@assert abs(solution.flux[1] - 0.1) < 1e-12 +@assert abs(solution.res[1] - (-2.0)) < 1e-12 + +println("✅ Residual 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/julia/test/test_solution.jl b/example/1d-linear-convection/weno3/julia/01f/julia/test/test_solution.jl new file mode 100644 index 00000000..f44ff0f0 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/julia/test/test_solution.jl @@ -0,0 +1,42 @@ +# julia/test/test_solution.jl +include("../mesh.jl") +include("../domain.jl") +include("../solution.jl") + +# MockConfig +struct MockConfig + recon_scheme::String + spatial_order::Int + ic_type::String + domain_length::Float64 + pulse_center::Float64 + pulse_width::Float64 +end + +# 创建 solution +config = MockConfig("eno", 2, "step", 2.0, 0.5, 0.1) +mesh = Mesh() +domain = Domain(config, mesh) +sol = Solution(config, domain) + +# 检查字段尺寸 +@assert length(sol.q_face_left) == mesh.nnodes # 41 +@assert length(sol.flux) == mesh.nnodes # 41 +@assert length(sol.res) == mesh.ncells # 40 +@assert length(sol.u) == domain.ntcells # 44 + +# 检查初始场 +println("u[3] (物理起始): ", sol.u[3]) # 应为 1.0 +println("u[23] (x=1.0): ", sol.u[23]) # 应为 2.0 + +# 测试 update_old_field +sol.u[3] = 999.0 +update_old_field(sol) +@assert sol.un[3] == 999.0 + +# 测试 reset_solution +reset_solution(sol) +@assert sol.u[3] == 0.0 +@assert sol.un[3] == 0.0 + +println("✅ Solution 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/julia/test_residual.jl b/example/1d-linear-convection/weno3/julia/01f/julia/test_residual.jl new file mode 100644 index 00000000..315d2aac --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/julia/test_residual.jl @@ -0,0 +1,37 @@ +# julia/test/test_mesh.jl +include("../mesh.jl") + +# 创建 mesh(与 Python 完全相同) +mesh = Mesh() + +# 打印关键值(与 Python 对比) +println("xmin = ", mesh.xmin) # 0.0 +println("xmax = ", mesh.xmax) # 2.0 +println("ncells = ", mesh.ncells) # 40 +println("nnodes = ", mesh.nnodes) # 41 +println("nx = ", mesh.nx) # 40 +println("L = ", mesh.L) # 2.0 +println("dx = ", mesh.dx) # 0.05 + +# 检查 x[1] (Python x[0]) 和 x[41] (Python x[40]) +println("x[1] = ", mesh.x[1]) # 0.0 +println("x[41] = ", mesh.x[41]) # 2.0 + +# 检查 xcc[1] (Python xcc[0]) 和 xcc[40] (Python xcc[39]) +println("xcc[1] = ", mesh.xcc[1]) # 0.025 +println("xcc[40] = ", mesh.xcc[40]) # 1.975 + +# ✅ 严格断言 +@assert mesh.xmin == 0.0 +@assert mesh.xmax == 2.0 +@assert mesh.ncells == 40 +@assert mesh.nnodes == 41 +@assert mesh.nx == 40 +@assert mesh.L == 2.0 +@assert mesh.dx == 0.05 +@assert mesh.x[1] == 0.0 +@assert mesh.x[41] == 2.0 +@assert abs(mesh.xcc[1] - 0.025) < 1e-12 +@assert abs(mesh.xcc[40] - 1.975) < 1e-12 + +println("✅ Mesh 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/python/boundary.py b/example/1d-linear-convection/weno3/julia/01f/python/boundary.py new file mode 100644 index 00000000..6054f92d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/python/boundary.py @@ -0,0 +1,103 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/python/cfd_registry.py b/example/1d-linear-convection/weno3/julia/01f/python/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/python/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/python/config.py b/example/1d-linear-convection/weno3/julia/01f/python/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/python/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/python/domain.py b/example/1d-linear-convection/weno3/julia/01f/python/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/python/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/python/factories/base_factory.py b/example/1d-linear-convection/weno3/julia/01f/python/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/python/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/python/flux.py b/example/1d-linear-convection/weno3/julia/01f/python/flux.py new file mode 100644 index 00000000..5ac73aa8 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/python/flux.py @@ -0,0 +1,74 @@ +# flux.py +""" +通量计算器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册,替代硬编码工厂 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象通量计算基类(统一接口) ---------------------- +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass + +# ---------------------- 2. 具体通量计算子类(使用装饰器注册) ---------------------- + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + +class FluxCalculatorFactory: + """通量计算器工厂""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD对象 + + Returns: + 通量计算器实例 + """ + from factories.base_factory import BaseFactory + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/python/gen_boundary_test_data.py b/example/1d-linear-convection/weno3/julia/01f/python/gen_boundary_test_data.py new file mode 100644 index 00000000..c7fc2a9c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/python/gen_boundary_test_data.py @@ -0,0 +1,35 @@ +# python/gen_boundary_test_data.py +import numpy as np +from config import CfdConfig +from mesh import Mesh +from domain import Domain + +# 固定测试配置 +config = CfdConfig() +config.with_boundary("dirichlet", left_value=0.5, right_value=1.5) +config.debug = True + +mesh = Mesh() +domain = Domain(config, mesh) + +# 构造 mock CFD 对象(仅含 config + domain) +class MockCfd: + def __init__(self, config, domain): + self.config = config + self.domain = domain + +# 测试用 u:0,1,2,...,N-1 +u_input = np.arange(domain.ntcells, dtype=np.float64) +np.save("u_input.npy", u_input) + +# 测试每种边界 +from boundary import PeriodicBoundary, DirichletBoundary, NeumannBoundary + +for bc_name, bc_class in [("periodic", PeriodicBoundary), ("dirichlet", DirichletBoundary), ("neumann", NeumannBoundary)]: + u = u_input.copy() + cfd_mock = MockCfd(config, domain) + bc = bc_class(cfd_mock) + bc.apply(u) + np.save(f"u_{bc_name}_py.npy", u) + +print("✅ 测试数据已生成:u_*.npy") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/python/gen_ic_test_data.py b/example/1d-linear-convection/weno3/julia/01f/python/gen_ic_test_data.py new file mode 100644 index 00000000..69c2573b --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/python/gen_ic_test_data.py @@ -0,0 +1,27 @@ +# python/gen_ic_test_data.py +import sys, os +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +import numpy as np +from config import CfdConfig +from mesh import Mesh +from domain import Domain +from solution import Solution + +# 固定 mesh +mesh = Mesh() +config = CfdConfig() + +# 测试三种 IC +for ic_type in ["step", "sin", "gaussian"]: + config.ic_type = ic_type + domain = Domain(config, mesh) + sol = Solution(config, domain) + + u_full = sol.u.copy() # 包含 ghost + u_interior = sol.u[domain.ist:domain.ied].copy() + + np.save(f"u_{ic_type}_full_py.npy", u_full) + np.save(f"u_{ic_type}_interior_py.npy", u_interior) + +print("✅ 初始条件测试数据已生成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/python/initial_condition.py b/example/1d-linear-convection/weno3/julia/01f/python/initial_condition.py new file mode 100644 index 00000000..047415b7 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/python/initial_condition.py @@ -0,0 +1,90 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, ic_type: str, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', ic_type, config) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/python/mesh.py b/example/1d-linear-convection/weno3/julia/01f/python/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/python/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/python/plotter.py b/example/1d-linear-convection/weno3/julia/01f/python/plotter.py new file mode 100644 index 00000000..9f1a414f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/python/plotter.py @@ -0,0 +1,107 @@ +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/python/reconstructor/__init__.py b/example/1d-linear-convection/weno3/julia/01f/python/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/python/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/python/reconstructor/base.py b/example/1d-linear-convection/weno3/julia/01f/python/reconstructor/base.py new file mode 100644 index 00000000..bbd63850 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/python/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def reconstruct(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/python/reconstructor/eno.py b/example/1d-linear-convection/weno3/julia/01f/python/reconstructor/eno.py new file mode 100644 index 00000000..c2fb385d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/python/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def reconstruct(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/python/reconstructor/factory.py b/example/1d-linear-convection/weno3/julia/01f/python/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/python/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/python/reconstructor/weno3.py b/example/1d-linear-convection/weno3/julia/01f/python/reconstructor/weno3.py new file mode 100644 index 00000000..6e8c3f23 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/python/reconstructor/weno3.py @@ -0,0 +1,88 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def reconstruct(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + """ + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 # smoothness indicator for stencil [v2, v3] + beta1 = (v2 - v1)**2 # smoothness indicator for stencil [v1, v2] + + d0, d1 = 2/3, 1/3 # optimal linear weights (for right value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 0.5 * v2 + 0.5 * v3 # reconstruction from [v2, v3] + q1 = -0.5 * v1 + 1.5 * v2 # reconstruction from [v1, v2] + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 + beta1 = (v2 - v1)**2 + + d0, d1 = 1/3, 2/3 # optimal linear weights (for left value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 1.5 * v2 - 0.5 * v3 # from [v2, v3] + q1 = 0.5 * v1 + 0.5 * v2 # from [v1, v2] + return w0 * q0 + w1 * q1 + """ diff --git a/example/1d-linear-convection/weno3/julia/01f/python/registry.py b/example/1d-linear-convection/weno3/julia/01f/python/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/python/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/python/residual.py b/example/1d-linear-convection/weno3/julia/01f/python/residual.py new file mode 100644 index 00000000..b4d4d7dc --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/python/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._reconstruct() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _reconstruct(self): + """私有方法:界面值重建""" + self.reconstructor.reconstruct(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/python/run_eno_weno.py b/example/1d-linear-convection/weno3/julia/01f/python/run_eno_weno.py new file mode 100644 index 00000000..fd7f3874 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/python/run_eno_weno.py @@ -0,0 +1,52 @@ +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/python/solution.py b/example/1d-linear-convection/weno3/julia/01f/python/solution.py new file mode 100644 index 00000000..92a46d3f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/python/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config.ic_type, config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/python/solver.py b/example/1d-linear-convection/weno3/julia/01f/python/solver.py new file mode 100644 index 00000000..0d0b442d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/python/solver.py @@ -0,0 +1,86 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + + +from flux import InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory + +from boundary import BoundaryCondition, PeriodicBoundary, DirichletBoundary, NeumannBoundary, BoundaryConditionFactory + +from time_integration import TimeIntegrator, RK1Integrator, RK2Integrator, RK3Integrator, TimeIntegratorFactory + +from mesh import Mesh + +from reconstructor import ReconstructorFactory + +from initial_condition import InitialCondition, StepFunctionIC, SineWaveIC, GaussianPulseIC, InitialConditionFactory + +from domain import Domain +from solution import Solution + +from config import CfdConfig +from residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config.ic_type, self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + #runge_kutta(self) + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/julia/01f/python/time_integration.py b/example/1d-linear-convection/weno3/julia/01f/python/time_integration.py new file mode 100644 index 00000000..25ac0b4c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01f/python/time_integration.py @@ -0,0 +1,125 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class TimeIntegratorFactory: + """时间推进器工厂""" + + @staticmethod + def create(cfd) -> 'TimeIntegrator': + """ + 创建时间推进器实例 + + Args: + cfd: CFD对象 + + Returns: + 时间推进器实例 + """ + from factories.base_factory import BaseFactory + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01f/python/u_dirichlet_py.npy b/example/1d-linear-convection/weno3/julia/01f/python/u_dirichlet_py.npy new file mode 100644 index 00000000..3aa20bd2 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01f/python/u_dirichlet_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01f/python/u_gaussian_full_py.npy b/example/1d-linear-convection/weno3/julia/01f/python/u_gaussian_full_py.npy new file mode 100644 index 00000000..b762f0fe Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01f/python/u_gaussian_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01f/python/u_gaussian_interior_py.npy b/example/1d-linear-convection/weno3/julia/01f/python/u_gaussian_interior_py.npy new file mode 100644 index 00000000..68af8954 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01f/python/u_gaussian_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01f/python/u_input.npy b/example/1d-linear-convection/weno3/julia/01f/python/u_input.npy new file mode 100644 index 00000000..ef506fa7 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01f/python/u_input.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01f/python/u_neumann_py.npy b/example/1d-linear-convection/weno3/julia/01f/python/u_neumann_py.npy new file mode 100644 index 00000000..fa723d1e Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01f/python/u_neumann_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01f/python/u_periodic_py.npy b/example/1d-linear-convection/weno3/julia/01f/python/u_periodic_py.npy new file mode 100644 index 00000000..156aff85 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01f/python/u_periodic_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01f/python/u_sin_full_py.npy b/example/1d-linear-convection/weno3/julia/01f/python/u_sin_full_py.npy new file mode 100644 index 00000000..b87f8a13 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01f/python/u_sin_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01f/python/u_sin_interior_py.npy b/example/1d-linear-convection/weno3/julia/01f/python/u_sin_interior_py.npy new file mode 100644 index 00000000..6803fbfb Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01f/python/u_sin_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01f/python/u_step_full_py.npy b/example/1d-linear-convection/weno3/julia/01f/python/u_step_full_py.npy new file mode 100644 index 00000000..2fc0e183 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01f/python/u_step_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01f/python/u_step_interior_py.npy b/example/1d-linear-convection/weno3/julia/01f/python/u_step_interior_py.npy new file mode 100644 index 00000000..b5ad6600 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01f/python/u_step_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01g/julia/boundary.jl b/example/1d-linear-convection/weno3/julia/01g/julia/boundary.jl new file mode 100644 index 00000000..5b0baaf4 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/julia/boundary.jl @@ -0,0 +1,90 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/julia/domain.jl b/example/1d-linear-convection/weno3/julia/01g/julia/domain.jl new file mode 100644 index 00000000..a7edc226 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/julia/domain.jl @@ -0,0 +1,61 @@ +# julia/domain.jl +""" +Domain 结构体(与 domain.py 完全同构) +- 保存 config +- nghosts 计算逻辑 1:1 +- ist/ied 为 0-based(与 Python 一致) +""" + +include("mesh.jl") + +mutable struct Domain + config::Any # 保存 config(与 Python 一致) + mesh::Mesh + nghosts::Int + ist::Int # 0-based + ied::Int # 0-based + ntcells::Int +end + +function Domain(config::Any, mesh::Mesh) + nghosts = _calc_nghosts(config) + ist = nghosts + 1 # ← 1-based 起始索引 + ied = ist + mesh.ncells # ← 1-based 结束索引(不包含) + ntcells = mesh.ncells + 2 * nghosts + Domain(config, mesh, nghosts, ist, ied, ntcells) +end + +function _calc_nghosts(config::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + order = getfield_safe(config, :spatial_order, 2) + + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + if scheme == "eno" + nghosts = order + elseif startswith(scheme, "weno") + nghosts = order ÷ 2 + 1 + else + error("未知重建格式 $(scheme),无法计算ghost层!") + end + + if nghosts <= 0 + error("计算得到的ghost层数量无效:$(nghosts)(阶数$(order),格式$(scheme))") + end + + return nghosts +end + +function is_physical_cell(domain::Domain, idx::Int) + return domain.ist <= idx < domain.ied +end + +function get_physical_indices(domain::Domain) + return domain.ist:(domain.ied - 1) +end + +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/julia/flux.jl b/example/1d-linear-convection/weno3/julia/01g/julia/flux.jl new file mode 100644 index 00000000..047598f7 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/julia/flux.jl @@ -0,0 +1,70 @@ +# julia/flux.jl +""" +通量计算器模块(与 flux.py 完全同构) +- 抽象基类 + 具体实现 +- 字段:cfd, config, mesh, wave_speed +""" + +include("mesh.jl") + +# ---------------------- 抽象基类 ---------------------- +""" +InviscidFluxCalculator 抽象类型 +Julia 无 ABC,用文档约定 +所有子类型必须实现 compute! +""" +abstract type InviscidFluxCalculator end + +# ---------------------- RusanovFluxCalculator ---------------------- +mutable struct RusanovFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function RusanovFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::RusanovFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = calc.wave_speed + c_R = calc.wave_speed + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + end +end + +# ---------------------- EngquistOsherFluxCalculator ---------------------- +mutable struct EngquistOsherFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function EngquistOsherFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::EngquistOsherFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + c = calc.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/julia/initial_condition.jl b/example/1d-linear-convection/weno3/julia/01g/julia/initial_condition.jl new file mode 100644 index 00000000..5e029e8d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/julia/initial_condition.jl @@ -0,0 +1,86 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/julia/mesh.jl b/example/1d-linear-convection/weno3/julia/01g/julia/mesh.jl new file mode 100644 index 00000000..1b0dd4bf --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/julia/mesh.jl @@ -0,0 +1,45 @@ +# julia/mesh.jl +""" +Mesh 结构体(与提供的 mesh.py 完全同构) +- 字段名、初始化顺序、循环逻辑完全一致 +- 不做任何泛化或优化 +""" + +mutable struct Mesh + xmin::Float64 + xmax::Float64 + ncells::Int + nnodes::Int + nx::Int + x::Vector{Float64} + xcc::Vector{Float64} + L::Float64 # 在 init_mesh 中赋值 + dx::Float64 # 在 init_mesh 中赋值 + + function Mesh() + # 与 Python __init__ 完全一致 + xmin = 0.0 + xmax = 2.0 + ncells = 40 + nnodes = ncells + 1 + nx = ncells + x = zeros(Float64, nnodes) + xcc = zeros(Float64, ncells) + + # 调用 init_mesh(模拟 Python) + L = xmax - xmin + dx = L / ncells + + # Generate node coordinates: for i in range(self.nnodes) + for i in 1:nnodes + x[i] = xmin + (i - 1) * dx # Python i → Julia i-1 + end + + # Generate cell center coordinates + for i in 1:ncells + xcc[i] = 0.5 * (x[i] + x[i+1]) + end + + new(xmin, xmax, ncells, nnodes, nx, x, xcc, L, dx) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/julia/residual.jl b/example/1d-linear-convection/weno3/julia/01g/julia/residual.jl new file mode 100644 index 00000000..15cc9387 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/julia/residual.jl @@ -0,0 +1,65 @@ +# julia/residual.jl +""" +残差计算器(与 residual.py 完全同构) +- 封装重建→通量→散度完整流程 +- 依赖 cfd 的多个字段 +""" + +include("mesh.jl") + +mutable struct ResidualCalculator + cfd::Any + config::Any + domain::Any + solution::Any + mesh::Mesh + reconstructor::Any + flux_calculator::Any # 通量计算器(外部传入,替代工厂) + + function ResidualCalculator(cfd::Any, flux_calculator::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + mesh = domain.mesh + reconstructor = cfd.reconstructor + + new(cfd, config, domain, solution, mesh, reconstructor, flux_calculator) + end +end + +""" +计算完整残差(对外唯一接口) +""" +function compute!(calc::ResidualCalculator) + #_reconstruct(calc) + _compute_inviscid_flux(calc) + _compute_flux_divergence(calc) +end + +""" +私有方法:界面值重建 +""" +function _reconstruct(calc::ResidualCalculator) + reconstruct(calc.reconstructor, calc.solution.u, calc.cfd) +end + +""" +私有方法:计算无粘通量 +""" +function _compute_inviscid_flux(calc::ResidualCalculator) + compute!(calc.flux_calculator, + calc.solution.q_face_left, + calc.solution.q_face_right, + calc.solution.flux) +end + +""" +私有方法:计算通量散度(残差 = -dF/dx) +""" +function _compute_flux_divergence(calc::ResidualCalculator) + solution = calc.solution + mesh = calc.mesh + for i in 1:mesh.ncells + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / mesh.dx + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/julia/solution.jl b/example/1d-linear-convection/weno3/julia/01g/julia/solution.jl new file mode 100644 index 00000000..90ca0393 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/julia/solution.jl @@ -0,0 +1,75 @@ +# julia/solution.jl +""" +Solution 结构体(与真实 solution.py 完全同构) +字段顺序、方法、逻辑 1:1 对齐 +""" + +include("mesh.jl") +include("domain.jl") +include("initial_condition.jl") + +mutable struct Solution + domain::Domain + q_face_left::Vector{Float64} + q_face_right::Vector{Float64} + flux::Vector{Float64} + res::Vector{Float64} + u::Vector{Float64} + un::Vector{Float64} + + function Solution(config::Any, domain::Domain) + mesh = domain.mesh + + q_face_left = zeros(Float64, mesh.nnodes) + q_face_right = zeros(Float64, mesh.nnodes) + flux = zeros(Float64, mesh.nnodes) + res = zeros(Float64, mesh.ncells) + u = zeros(Float64, domain.ntcells) + un = zeros(Float64, domain.ntcells) + + sol = new(domain, q_face_left, q_face_right, flux, res, u, un) + initialize_from_config(sol, config) + return sol + end +end + +""" +重置解数组为初始状态 +""" +function reset_solution(sol::Solution) + fill!(sol.u, 0.0) + fill!(sol.un, 0.0) +end + +""" +根据配置初始化场 +注:暂用硬编码替代 InitialConditionFactory +""" +function initialize_from_config(sol::Solution, config::Any) + ic_type = getfield_safe(config, :ic_type, "step") + + # 硬编码创建 IC(替代 InitialConditionFactory) + ic = if ic_type == "step" + StepFunctionIC(config) + elseif ic_type == "sin" + SineWaveIC(config) + elseif ic_type == "gaussian" + GaussianPulseIC(config) + else + error("未知初始条件类型: $ic_type") + end + + apply(ic, sol) # 调用 IC.apply +end + +""" +更新旧场:un = u +""" +function update_old_field(sol::Solution) + sol.un .= sol.u # 等价于 Python 的 un[:] = u[:] +end + +# ---------------------- 辅助函数 ---------------------- +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/julia/test/test_boundary.jl b/example/1d-linear-convection/weno3/julia/01g/julia/test/test_boundary.jl new file mode 100644 index 00000000..ef27d682 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/julia/test/test_boundary.jl @@ -0,0 +1,57 @@ +# julia/test/test_boundary.jl +using NPZ +include("../boundary.jl") + +struct MockConfig + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + debug::Bool +end + +struct MockDomain + nghosts::Int + ist::Int # Julia 本地索引(1-based) + ied::Int + ntcells::Int +end + +struct MockCfd + config::MockConfig + domain::MockDomain +end + +# ===== 关键:使用 Julia 本地索引规则 ===== +nghosts = 2 +ncells = 40 +ist = nghosts + 1 # = 3 +ied = ist + ncells # = 43 +ntcells = ncells + 2 * nghosts # = 44 + +config = MockConfig("dirichlet", 0.5, 1.5, true) +domain = MockDomain(nghosts, ist, ied, ntcells) +cfd_mock = MockCfd(config, domain) + +# 加载 Python 生成的 u_input.npy +# 注意:Python u[0] → Julia u[1],所以内容完全对应 +u_input = npzread("../../python/u_input.npy") +@assert length(u_input) == ntcells "数组长度不匹配!" + +# 测试三种边界 +test_cases = [ + ("periodic", PeriodicBoundary(cfd_mock)), + ("dirichlet", DirichletBoundary(cfd_mock)), + ("neumann", NeumannBoundary(cfd_mock)) +] + +for (name, bc) in test_cases + u = copy(u_input) + apply!(bc, u) + + u_py = npzread("../../python/u_$(name)_py.npy") + err = maximum(abs.(u .- u_py)) + println("边界: $(name) | 最大误差: $(err)") + @assert err < 1e-12 "❌ $(name) 不一致!" +end + +println("✅ 所有边界条件测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/julia/test/test_domain.jl b/example/1d-linear-convection/weno3/julia/01g/julia/test/test_domain.jl new file mode 100644 index 00000000..7b423f77 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/julia/test/test_domain.jl @@ -0,0 +1,44 @@ +# julia/test/test_domain.jl +include("../mesh.jl") +include("../domain.jl") + +# MockConfig:模拟 Python CfdConfig +struct MockConfig + recon_scheme::String + spatial_order::Int +end + +# 测试 ENO +config_eno = MockConfig("eno", 2) +mesh = Mesh() +domain_eno = Domain(config_eno, mesh) + +println("ENO: nghosts = ", domain_eno.nghosts) # 2 +println("ENO: ist = ", domain_eno.ist) # 2 +println("ENO: ied = ", domain_eno.ied) # 42 +println("物理索引范围: ", collect(get_physical_indices(domain_eno))[1:3], " ... ", collect(get_physical_indices(domain_eno))[end-2:end]) + +# 测试 WENO(字符串 "weno") +config_weno = MockConfig("weno", 2) +domain_weno = Domain(config_weno, mesh) +println("WENO: nghosts = ", domain_weno.nghosts) # 2 + +# 测试 WENO3(字符串 "weno3") +config_weno3 = MockConfig("weno3", 2) +domain_weno3 = Domain(config_weno3, mesh) +println("WENO3: nghosts = ", domain_weno3.nghosts) # 2 + +# 测试 is_physical_cell +@assert is_physical_cell(domain_eno, 2) == true # ist=2 +@assert is_physical_cell(domain_eno, 41) == true # ied-1=41 +@assert is_physical_cell(domain_eno, 42) == false # ied=42 + +# ✅ 断言 +@assert domain_eno.nghosts == 2 +@assert domain_eno.ist == 2 +@assert domain_eno.ied == 42 +@assert domain_eno.ntcells == 44 +@assert domain_weno.nghosts == 2 +@assert domain_weno3.nghosts == 2 + +println("✅ Domain 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/julia/test/test_flux.jl b/example/1d-linear-convection/weno3/julia/01g/julia/test/test_flux.jl new file mode 100644 index 00000000..0ebc1dc0 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/julia/test/test_flux.jl @@ -0,0 +1,53 @@ +# julia/test/test_flux.jl +include("../mesh.jl") +include("../flux.jl") + +# Mock 对象 +struct MockConfig + flux_type::String + wave_speed::Float64 +end + +struct MockDomain + mesh::Mesh +end + +struct MockCfd + config::MockConfig + domain::MockDomain +end + +# 创建 mock +mesh = Mesh() +config = MockConfig("rusanov", 1.0) +domain = MockDomain(mesh) +cfd = MockCfd(config, domain) + +# 测试 Rusanov +rusanov = RusanovFluxCalculator(cfd) +N = mesh.nnodes +qL = [1.0, 2.0, 3.0, 4.0, 5.0, zeros(Float64, N-5)...] +qR = [2.0, 3.0, 4.0, 5.0, 6.0, zeros(Float64, N-5)...] +flux_rus = zeros(Float64, N) +compute!(rusanov, qL, qR, flux_rus) + +println("Rusanov flux[1] = ", flux_rus[1]) # 应为 1.0 +@assert abs(flux_rus[1] - 1.0) < 1e-12 + +# 测试 Engquist-Osher +eo = EngquistOsherFluxCalculator(cfd) +flux_eo = zeros(Float64, N) +compute!(eo, qL, qR, flux_eo) + +println("EO flux[1] = ", flux_eo[1]) # c=1 → cp=1, cm=0 → flux=1*1 + 0*2 = 1.0 +@assert abs(flux_eo[1] - 1.0) < 1e-12 + +# 测试 c = -1.0 +config_neg = MockConfig("rusanov", -1.0) +cfd_neg = MockCfd(config_neg, domain) +rusanov_neg = RusanovFluxCalculator(cfd_neg) +compute!(rusanov_neg, qL, qR, flux_rus) +println("Rusanov (c=-1) flux[1] = ", flux_rus[1]) # F_L=-1, F_R=-2, Smax=1 → flux = -1.5 -0.5*(-1) = -1.0 +@assert abs(flux_rus[1] - (-2.0)) < 1e-12 # ← 修正:-2.0 而非 -1.0 + +println("✅ Flux 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/julia/test/test_initial_condition.jl b/example/1d-linear-convection/weno3/julia/01g/julia/test/test_initial_condition.jl new file mode 100644 index 00000000..dc58691a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/julia/test/test_initial_condition.jl @@ -0,0 +1,45 @@ +# julia/test/test_initial_condition.jl +using NPZ + +# 包含初始条件模块 +include("../initial_condition.jl") + +# 构造 mock config(与 Python 完全一致) +struct MockConfig + ic_type::String + domain_length::Float64 + pulse_center::Float64 + pulse_width::Float64 +end + +# 生成与 Python Mesh.xcc 完全相同的 x 坐标 +function generate_xcc() + xmin, xmax = 0.0, 2.0 + ncells = 40 + dx = (xmax - xmin) / ncells + xcc = Vector{Float64}(undef, ncells) + for i in 1:ncells + xcc[i] = xmin + (i - 0.5) * dx # i-1 + 0.5 → i-0.5 + end + return xcc +end + +# 主测试 +xcc = generate_xcc() + +test_cases = [ + ("step", StepFunctionIC(MockConfig("step", 2.0, 0.5, 0.1))), + ("sin", SineWaveIC(MockConfig("sin", 2.0, 0.5, 0.1))), + ("gaussian", GaussianPulseIC(MockConfig("gaussian", 2.0, 0.5, 0.1))) +] + +for (name, ic) in test_cases + u_jl = evaluate_at(ic, xcc) + u_py = npzread("../../python/u_$(name)_interior_py.npy") + + err = maximum(abs.(u_jl .- u_py)) + println("IC: $(name) | 最大误差: $(err)") + @assert err < 1e-12 "❌ $(name) 不一致!" +end + +println("✅ 所有初始条件测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/julia/test/test_mesh.jl b/example/1d-linear-convection/weno3/julia/01g/julia/test/test_mesh.jl new file mode 100644 index 00000000..315d2aac --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/julia/test/test_mesh.jl @@ -0,0 +1,37 @@ +# julia/test/test_mesh.jl +include("../mesh.jl") + +# 创建 mesh(与 Python 完全相同) +mesh = Mesh() + +# 打印关键值(与 Python 对比) +println("xmin = ", mesh.xmin) # 0.0 +println("xmax = ", mesh.xmax) # 2.0 +println("ncells = ", mesh.ncells) # 40 +println("nnodes = ", mesh.nnodes) # 41 +println("nx = ", mesh.nx) # 40 +println("L = ", mesh.L) # 2.0 +println("dx = ", mesh.dx) # 0.05 + +# 检查 x[1] (Python x[0]) 和 x[41] (Python x[40]) +println("x[1] = ", mesh.x[1]) # 0.0 +println("x[41] = ", mesh.x[41]) # 2.0 + +# 检查 xcc[1] (Python xcc[0]) 和 xcc[40] (Python xcc[39]) +println("xcc[1] = ", mesh.xcc[1]) # 0.025 +println("xcc[40] = ", mesh.xcc[40]) # 1.975 + +# ✅ 严格断言 +@assert mesh.xmin == 0.0 +@assert mesh.xmax == 2.0 +@assert mesh.ncells == 40 +@assert mesh.nnodes == 41 +@assert mesh.nx == 40 +@assert mesh.L == 2.0 +@assert mesh.dx == 0.05 +@assert mesh.x[1] == 0.0 +@assert mesh.x[41] == 2.0 +@assert abs(mesh.xcc[1] - 0.025) < 1e-12 +@assert abs(mesh.xcc[40] - 1.975) < 1e-12 + +println("✅ Mesh 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/julia/test/test_residual.jl b/example/1d-linear-convection/weno3/julia/01g/julia/test/test_residual.jl new file mode 100644 index 00000000..e37e8d2b --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/julia/test/test_residual.jl @@ -0,0 +1,53 @@ +# julia/test/test_residual.jl +include("../mesh.jl") +include("../domain.jl") +include("../solution.jl") +include("../flux.jl") +include("../residual.jl") + +# ===== Dummy Reconstructor ===== +struct DummyReconstructor end + +# ===== Mock Config ===== +struct MockConfig + flux_type::String + wave_speed::Float64 +end + +# ===== Mock CFD (必须包含 reconstructor 字段) ===== +struct MockCfd + config::MockConfig + domain::Domain + solution::Solution + reconstructor::DummyReconstructor # ← 关键:添加此字段 +end + +# ===== 主测试 ===== +config = MockConfig("rusanov", 1.0) +mesh = Mesh() +domain = Domain((recon_scheme="eno", spatial_order=2), mesh) +solution = Solution((ic_type="step",), domain) + +# 手动设置界面值(跳过重建) +for i in 1:mesh.nnodes + solution.q_face_left[i] = Float64(i) * 0.1 + solution.q_face_right[i] = Float64(i) * 0.1 +end + +flux_calc = RusanovFluxCalculator((config=config, domain=domain)) +dummy_recon = DummyReconstructor() +cfd = MockCfd(config, domain, solution, dummy_recon) + +# 创建残差计算器 +res_calc = ResidualCalculator(cfd, flux_calc) + +# 计算残差(注意:_reconstruct 仍被注释) +compute!(res_calc) + +println("flux[1] = ", solution.flux[1]) +println("res[1] = ", solution.res[1]) + +@assert abs(solution.flux[1] - 0.1) < 1e-12 +@assert abs(solution.res[1] - (-2.0)) < 1e-12 + +println("✅ Residual 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/julia/test/test_solution.jl b/example/1d-linear-convection/weno3/julia/01g/julia/test/test_solution.jl new file mode 100644 index 00000000..f44ff0f0 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/julia/test/test_solution.jl @@ -0,0 +1,42 @@ +# julia/test/test_solution.jl +include("../mesh.jl") +include("../domain.jl") +include("../solution.jl") + +# MockConfig +struct MockConfig + recon_scheme::String + spatial_order::Int + ic_type::String + domain_length::Float64 + pulse_center::Float64 + pulse_width::Float64 +end + +# 创建 solution +config = MockConfig("eno", 2, "step", 2.0, 0.5, 0.1) +mesh = Mesh() +domain = Domain(config, mesh) +sol = Solution(config, domain) + +# 检查字段尺寸 +@assert length(sol.q_face_left) == mesh.nnodes # 41 +@assert length(sol.flux) == mesh.nnodes # 41 +@assert length(sol.res) == mesh.ncells # 40 +@assert length(sol.u) == domain.ntcells # 44 + +# 检查初始场 +println("u[3] (物理起始): ", sol.u[3]) # 应为 1.0 +println("u[23] (x=1.0): ", sol.u[23]) # 应为 2.0 + +# 测试 update_old_field +sol.u[3] = 999.0 +update_old_field(sol) +@assert sol.un[3] == 999.0 + +# 测试 reset_solution +reset_solution(sol) +@assert sol.u[3] == 0.0 +@assert sol.un[3] == 0.0 + +println("✅ Solution 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/julia/test/test_time_integration.jl b/example/1d-linear-convection/weno3/julia/01g/julia/test/test_time_integration.jl new file mode 100644 index 00000000..091a8637 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/julia/test/test_time_integration.jl @@ -0,0 +1,53 @@ +# julia/test/test_time_integration.jl +include("../time_integration.jl") +include("../flux.jl") +include("../boundary.jl") + +# Mock CFD 对象 +struct MockCfd + config::Any + domain::Domain + solution::Solution + residual_calculator::Any + boundary_condition::Any +end + +# 创建完整链路 +mesh = Mesh() +domain = Domain((recon_scheme="eno", spatial_order=2), mesh) +solution = Solution((ic_type="step",), domain) + +# 手动设置界面值(跳过 reconstructor) +for i in 1:mesh.nnodes + solution.q_face_left[i] = Float64(i) * 0.1 + solution.q_face_right[i] = Float64(i) * 0.1 +end + +# Mock components +flux_calc = RusanovFluxCalculator((config=(wave_speed=1.0,), domain=domain)) +res_calc = ResidualCalculator((config=nothing, domain=domain, solution=solution, reconstructor=nothing), flux_calc) +bc = PeriodicBoundary((domain=domain, config=(debug=false,))) +cfd = MockCfd((rk_order=1,), domain, solution, res_calc, bc) + +# 测试 RK1 +rk1 = RK1Integrator(cfd) +step(rk1, 0.025) + +println("RK1 step completed") +@assert solution.u[3] != 0.0 # 应已更新 + +# 测试 RK2 +cfd2 = MockCfd((rk_order=2,), domain, solution, res_calc, bc) +rk2 = RK2Integrator(cfd2) +step(rk2, 0.025) + +println("RK2 step completed") + +# 测试 RK3 +cfd3 = MockCfd((rk_order=3,), domain, solution, res_calc, bc) +rk3 = RK3Integrator(cfd3) +step(rk3, 0.025) + +println("RK3 step completed") + +println("✅ Time Integration 逻辑测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/julia/test_residual.jl b/example/1d-linear-convection/weno3/julia/01g/julia/test_residual.jl new file mode 100644 index 00000000..315d2aac --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/julia/test_residual.jl @@ -0,0 +1,37 @@ +# julia/test/test_mesh.jl +include("../mesh.jl") + +# 创建 mesh(与 Python 完全相同) +mesh = Mesh() + +# 打印关键值(与 Python 对比) +println("xmin = ", mesh.xmin) # 0.0 +println("xmax = ", mesh.xmax) # 2.0 +println("ncells = ", mesh.ncells) # 40 +println("nnodes = ", mesh.nnodes) # 41 +println("nx = ", mesh.nx) # 40 +println("L = ", mesh.L) # 2.0 +println("dx = ", mesh.dx) # 0.05 + +# 检查 x[1] (Python x[0]) 和 x[41] (Python x[40]) +println("x[1] = ", mesh.x[1]) # 0.0 +println("x[41] = ", mesh.x[41]) # 2.0 + +# 检查 xcc[1] (Python xcc[0]) 和 xcc[40] (Python xcc[39]) +println("xcc[1] = ", mesh.xcc[1]) # 0.025 +println("xcc[40] = ", mesh.xcc[40]) # 1.975 + +# ✅ 严格断言 +@assert mesh.xmin == 0.0 +@assert mesh.xmax == 2.0 +@assert mesh.ncells == 40 +@assert mesh.nnodes == 41 +@assert mesh.nx == 40 +@assert mesh.L == 2.0 +@assert mesh.dx == 0.05 +@assert mesh.x[1] == 0.0 +@assert mesh.x[41] == 2.0 +@assert abs(mesh.xcc[1] - 0.025) < 1e-12 +@assert abs(mesh.xcc[40] - 1.975) < 1e-12 + +println("✅ Mesh 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/julia/time_integration.jl b/example/1d-linear-convection/weno3/julia/01g/julia/time_integration.jl new file mode 100644 index 00000000..95e5cd67 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/julia/time_integration.jl @@ -0,0 +1,130 @@ +# julia/time_integration.jl +""" +时间推进器模块(与 time_integration.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("residual.jl") +include("boundary.jl") + +# ---------------------- 抽象时间推进器基类 ---------------------- +abstract type TimeIntegrator end + +mutable struct TimeIntegratorBase <: TimeIntegrator + cfd::Any + config::Any + domain::Domain + solution::Solution + residual_calculator::Any # ResidualCalculator +end + +function TimeIntegratorBase(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + residual_calculator = cfd.residual_calculator + TimeIntegratorBase(cfd, config, domain, solution, residual_calculator) +end + +function compute_residual(integrator::TimeIntegratorBase) + compute!(integrator.residual_calculator) +end + +function apply_boundary(integrator::TimeIntegratorBase) + apply!(integrator.cfd.boundary_condition, integrator.solution.u) +end + +function map_idx(integrator::TimeIntegratorBase, i::Int) + return i - integrator.domain.ist + 1 # ← +1 转为 1-based +end + +# ---------------------- RK1Integrator ---------------------- +mutable struct RK1Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK1Integrator(cfd::Any) + RK1Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK1Integrator, dt::Float64) + compute_residual(integrator.base) + for i in integrator.base.domain.ist:(integrator.base.domain.ied - 1) + j = map_idx(integrator.base, i) + integrator.base.solution.u[i] += dt * integrator.base.solution.res[j] + end + apply_boundary(integrator.base) + update_old_field(integrator.base.solution) +end + +# ---------------------- RK2Integrator ---------------------- +mutable struct RK2Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK2Integrator(cfd::Any) + RK2Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK2Integrator, dt::Float64) + base = integrator.base + # 阶段1:预测步 + compute_residual(base) + u_pred = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u_pred[i] += dt * base.solution.res[j] + end + base.solution.u .= u_pred + apply_boundary(base) + # 阶段2:校正步 + compute_residual(base) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = 0.5 * base.solution.un[i] + 0.5 * base.solution.u[i] + 0.5 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- RK3Integrator ---------------------- +mutable struct RK3Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK3Integrator(cfd::Any) + RK3Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK3Integrator, dt::Float64) + base = integrator.base + # 阶段1 + compute_residual(base) + u1 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u1[i] += dt * base.solution.res[j] + end + base.solution.u .= u1 + apply_boundary(base) + # 阶段2 + compute_residual(base) + u2 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u2[i] = 0.75 * base.solution.un[i] + 0.25 * base.solution.u[i] + 0.25 * dt * base.solution.res[j] + end + base.solution.u .= u2 + apply_boundary(base) + # 阶段3 + compute_residual(base) + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = c1 * base.solution.un[i] + c2 * base.solution.u[i] + c3 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/python/boundary.py b/example/1d-linear-convection/weno3/julia/01g/python/boundary.py new file mode 100644 index 00000000..6054f92d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/python/boundary.py @@ -0,0 +1,103 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/python/cfd_registry.py b/example/1d-linear-convection/weno3/julia/01g/python/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/python/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/python/config.py b/example/1d-linear-convection/weno3/julia/01g/python/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/python/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/python/domain.py b/example/1d-linear-convection/weno3/julia/01g/python/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/python/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/python/factories/base_factory.py b/example/1d-linear-convection/weno3/julia/01g/python/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/python/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/python/flux.py b/example/1d-linear-convection/weno3/julia/01g/python/flux.py new file mode 100644 index 00000000..5ac73aa8 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/python/flux.py @@ -0,0 +1,74 @@ +# flux.py +""" +通量计算器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册,替代硬编码工厂 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象通量计算基类(统一接口) ---------------------- +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass + +# ---------------------- 2. 具体通量计算子类(使用装饰器注册) ---------------------- + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + +class FluxCalculatorFactory: + """通量计算器工厂""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD对象 + + Returns: + 通量计算器实例 + """ + from factories.base_factory import BaseFactory + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/python/gen_boundary_test_data.py b/example/1d-linear-convection/weno3/julia/01g/python/gen_boundary_test_data.py new file mode 100644 index 00000000..c7fc2a9c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/python/gen_boundary_test_data.py @@ -0,0 +1,35 @@ +# python/gen_boundary_test_data.py +import numpy as np +from config import CfdConfig +from mesh import Mesh +from domain import Domain + +# 固定测试配置 +config = CfdConfig() +config.with_boundary("dirichlet", left_value=0.5, right_value=1.5) +config.debug = True + +mesh = Mesh() +domain = Domain(config, mesh) + +# 构造 mock CFD 对象(仅含 config + domain) +class MockCfd: + def __init__(self, config, domain): + self.config = config + self.domain = domain + +# 测试用 u:0,1,2,...,N-1 +u_input = np.arange(domain.ntcells, dtype=np.float64) +np.save("u_input.npy", u_input) + +# 测试每种边界 +from boundary import PeriodicBoundary, DirichletBoundary, NeumannBoundary + +for bc_name, bc_class in [("periodic", PeriodicBoundary), ("dirichlet", DirichletBoundary), ("neumann", NeumannBoundary)]: + u = u_input.copy() + cfd_mock = MockCfd(config, domain) + bc = bc_class(cfd_mock) + bc.apply(u) + np.save(f"u_{bc_name}_py.npy", u) + +print("✅ 测试数据已生成:u_*.npy") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/python/gen_ic_test_data.py b/example/1d-linear-convection/weno3/julia/01g/python/gen_ic_test_data.py new file mode 100644 index 00000000..69c2573b --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/python/gen_ic_test_data.py @@ -0,0 +1,27 @@ +# python/gen_ic_test_data.py +import sys, os +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +import numpy as np +from config import CfdConfig +from mesh import Mesh +from domain import Domain +from solution import Solution + +# 固定 mesh +mesh = Mesh() +config = CfdConfig() + +# 测试三种 IC +for ic_type in ["step", "sin", "gaussian"]: + config.ic_type = ic_type + domain = Domain(config, mesh) + sol = Solution(config, domain) + + u_full = sol.u.copy() # 包含 ghost + u_interior = sol.u[domain.ist:domain.ied].copy() + + np.save(f"u_{ic_type}_full_py.npy", u_full) + np.save(f"u_{ic_type}_interior_py.npy", u_interior) + +print("✅ 初始条件测试数据已生成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/python/initial_condition.py b/example/1d-linear-convection/weno3/julia/01g/python/initial_condition.py new file mode 100644 index 00000000..047415b7 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/python/initial_condition.py @@ -0,0 +1,90 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, ic_type: str, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', ic_type, config) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/python/mesh.py b/example/1d-linear-convection/weno3/julia/01g/python/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/python/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/python/plotter.py b/example/1d-linear-convection/weno3/julia/01g/python/plotter.py new file mode 100644 index 00000000..9f1a414f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/python/plotter.py @@ -0,0 +1,107 @@ +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/python/reconstructor/__init__.py b/example/1d-linear-convection/weno3/julia/01g/python/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/python/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/python/reconstructor/base.py b/example/1d-linear-convection/weno3/julia/01g/python/reconstructor/base.py new file mode 100644 index 00000000..bbd63850 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/python/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def reconstruct(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/python/reconstructor/eno.py b/example/1d-linear-convection/weno3/julia/01g/python/reconstructor/eno.py new file mode 100644 index 00000000..c2fb385d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/python/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def reconstruct(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/python/reconstructor/factory.py b/example/1d-linear-convection/weno3/julia/01g/python/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/python/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/python/reconstructor/weno3.py b/example/1d-linear-convection/weno3/julia/01g/python/reconstructor/weno3.py new file mode 100644 index 00000000..6e8c3f23 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/python/reconstructor/weno3.py @@ -0,0 +1,88 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def reconstruct(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + """ + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 # smoothness indicator for stencil [v2, v3] + beta1 = (v2 - v1)**2 # smoothness indicator for stencil [v1, v2] + + d0, d1 = 2/3, 1/3 # optimal linear weights (for right value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 0.5 * v2 + 0.5 * v3 # reconstruction from [v2, v3] + q1 = -0.5 * v1 + 1.5 * v2 # reconstruction from [v1, v2] + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 + beta1 = (v2 - v1)**2 + + d0, d1 = 1/3, 2/3 # optimal linear weights (for left value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 1.5 * v2 - 0.5 * v3 # from [v2, v3] + q1 = 0.5 * v1 + 0.5 * v2 # from [v1, v2] + return w0 * q0 + w1 * q1 + """ diff --git a/example/1d-linear-convection/weno3/julia/01g/python/registry.py b/example/1d-linear-convection/weno3/julia/01g/python/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/python/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/python/residual.py b/example/1d-linear-convection/weno3/julia/01g/python/residual.py new file mode 100644 index 00000000..b4d4d7dc --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/python/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._reconstruct() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _reconstruct(self): + """私有方法:界面值重建""" + self.reconstructor.reconstruct(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/python/run_eno_weno.py b/example/1d-linear-convection/weno3/julia/01g/python/run_eno_weno.py new file mode 100644 index 00000000..fd7f3874 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/python/run_eno_weno.py @@ -0,0 +1,52 @@ +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/python/solution.py b/example/1d-linear-convection/weno3/julia/01g/python/solution.py new file mode 100644 index 00000000..92a46d3f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/python/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config.ic_type, config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/python/solver.py b/example/1d-linear-convection/weno3/julia/01g/python/solver.py new file mode 100644 index 00000000..0d0b442d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/python/solver.py @@ -0,0 +1,86 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + + +from flux import InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory + +from boundary import BoundaryCondition, PeriodicBoundary, DirichletBoundary, NeumannBoundary, BoundaryConditionFactory + +from time_integration import TimeIntegrator, RK1Integrator, RK2Integrator, RK3Integrator, TimeIntegratorFactory + +from mesh import Mesh + +from reconstructor import ReconstructorFactory + +from initial_condition import InitialCondition, StepFunctionIC, SineWaveIC, GaussianPulseIC, InitialConditionFactory + +from domain import Domain +from solution import Solution + +from config import CfdConfig +from residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config.ic_type, self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + #runge_kutta(self) + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/julia/01g/python/time_integration.py b/example/1d-linear-convection/weno3/julia/01g/python/time_integration.py new file mode 100644 index 00000000..25ac0b4c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01g/python/time_integration.py @@ -0,0 +1,125 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class TimeIntegratorFactory: + """时间推进器工厂""" + + @staticmethod + def create(cfd) -> 'TimeIntegrator': + """ + 创建时间推进器实例 + + Args: + cfd: CFD对象 + + Returns: + 时间推进器实例 + """ + from factories.base_factory import BaseFactory + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01g/python/u_dirichlet_py.npy b/example/1d-linear-convection/weno3/julia/01g/python/u_dirichlet_py.npy new file mode 100644 index 00000000..3aa20bd2 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01g/python/u_dirichlet_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01g/python/u_gaussian_full_py.npy b/example/1d-linear-convection/weno3/julia/01g/python/u_gaussian_full_py.npy new file mode 100644 index 00000000..b762f0fe Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01g/python/u_gaussian_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01g/python/u_gaussian_interior_py.npy b/example/1d-linear-convection/weno3/julia/01g/python/u_gaussian_interior_py.npy new file mode 100644 index 00000000..68af8954 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01g/python/u_gaussian_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01g/python/u_input.npy b/example/1d-linear-convection/weno3/julia/01g/python/u_input.npy new file mode 100644 index 00000000..ef506fa7 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01g/python/u_input.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01g/python/u_neumann_py.npy b/example/1d-linear-convection/weno3/julia/01g/python/u_neumann_py.npy new file mode 100644 index 00000000..fa723d1e Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01g/python/u_neumann_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01g/python/u_periodic_py.npy b/example/1d-linear-convection/weno3/julia/01g/python/u_periodic_py.npy new file mode 100644 index 00000000..156aff85 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01g/python/u_periodic_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01g/python/u_sin_full_py.npy b/example/1d-linear-convection/weno3/julia/01g/python/u_sin_full_py.npy new file mode 100644 index 00000000..b87f8a13 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01g/python/u_sin_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01g/python/u_sin_interior_py.npy b/example/1d-linear-convection/weno3/julia/01g/python/u_sin_interior_py.npy new file mode 100644 index 00000000..6803fbfb Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01g/python/u_sin_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01g/python/u_step_full_py.npy b/example/1d-linear-convection/weno3/julia/01g/python/u_step_full_py.npy new file mode 100644 index 00000000..2fc0e183 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01g/python/u_step_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01g/python/u_step_interior_py.npy b/example/1d-linear-convection/weno3/julia/01g/python/u_step_interior_py.npy new file mode 100644 index 00000000..b5ad6600 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01g/python/u_step_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01h/julia/boundary.jl b/example/1d-linear-convection/weno3/julia/01h/julia/boundary.jl new file mode 100644 index 00000000..5b0baaf4 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/julia/boundary.jl @@ -0,0 +1,90 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/julia/domain.jl b/example/1d-linear-convection/weno3/julia/01h/julia/domain.jl new file mode 100644 index 00000000..a7edc226 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/julia/domain.jl @@ -0,0 +1,61 @@ +# julia/domain.jl +""" +Domain 结构体(与 domain.py 完全同构) +- 保存 config +- nghosts 计算逻辑 1:1 +- ist/ied 为 0-based(与 Python 一致) +""" + +include("mesh.jl") + +mutable struct Domain + config::Any # 保存 config(与 Python 一致) + mesh::Mesh + nghosts::Int + ist::Int # 0-based + ied::Int # 0-based + ntcells::Int +end + +function Domain(config::Any, mesh::Mesh) + nghosts = _calc_nghosts(config) + ist = nghosts + 1 # ← 1-based 起始索引 + ied = ist + mesh.ncells # ← 1-based 结束索引(不包含) + ntcells = mesh.ncells + 2 * nghosts + Domain(config, mesh, nghosts, ist, ied, ntcells) +end + +function _calc_nghosts(config::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + order = getfield_safe(config, :spatial_order, 2) + + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + if scheme == "eno" + nghosts = order + elseif startswith(scheme, "weno") + nghosts = order ÷ 2 + 1 + else + error("未知重建格式 $(scheme),无法计算ghost层!") + end + + if nghosts <= 0 + error("计算得到的ghost层数量无效:$(nghosts)(阶数$(order),格式$(scheme))") + end + + return nghosts +end + +function is_physical_cell(domain::Domain, idx::Int) + return domain.ist <= idx < domain.ied +end + +function get_physical_indices(domain::Domain) + return domain.ist:(domain.ied - 1) +end + +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/julia/flux.jl b/example/1d-linear-convection/weno3/julia/01h/julia/flux.jl new file mode 100644 index 00000000..047598f7 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/julia/flux.jl @@ -0,0 +1,70 @@ +# julia/flux.jl +""" +通量计算器模块(与 flux.py 完全同构) +- 抽象基类 + 具体实现 +- 字段:cfd, config, mesh, wave_speed +""" + +include("mesh.jl") + +# ---------------------- 抽象基类 ---------------------- +""" +InviscidFluxCalculator 抽象类型 +Julia 无 ABC,用文档约定 +所有子类型必须实现 compute! +""" +abstract type InviscidFluxCalculator end + +# ---------------------- RusanovFluxCalculator ---------------------- +mutable struct RusanovFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function RusanovFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::RusanovFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = calc.wave_speed + c_R = calc.wave_speed + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + end +end + +# ---------------------- EngquistOsherFluxCalculator ---------------------- +mutable struct EngquistOsherFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function EngquistOsherFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::EngquistOsherFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + c = calc.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/julia/initial_condition.jl b/example/1d-linear-convection/weno3/julia/01h/julia/initial_condition.jl new file mode 100644 index 00000000..5e029e8d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/julia/initial_condition.jl @@ -0,0 +1,86 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/julia/mesh.jl b/example/1d-linear-convection/weno3/julia/01h/julia/mesh.jl new file mode 100644 index 00000000..1b0dd4bf --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/julia/mesh.jl @@ -0,0 +1,45 @@ +# julia/mesh.jl +""" +Mesh 结构体(与提供的 mesh.py 完全同构) +- 字段名、初始化顺序、循环逻辑完全一致 +- 不做任何泛化或优化 +""" + +mutable struct Mesh + xmin::Float64 + xmax::Float64 + ncells::Int + nnodes::Int + nx::Int + x::Vector{Float64} + xcc::Vector{Float64} + L::Float64 # 在 init_mesh 中赋值 + dx::Float64 # 在 init_mesh 中赋值 + + function Mesh() + # 与 Python __init__ 完全一致 + xmin = 0.0 + xmax = 2.0 + ncells = 40 + nnodes = ncells + 1 + nx = ncells + x = zeros(Float64, nnodes) + xcc = zeros(Float64, ncells) + + # 调用 init_mesh(模拟 Python) + L = xmax - xmin + dx = L / ncells + + # Generate node coordinates: for i in range(self.nnodes) + for i in 1:nnodes + x[i] = xmin + (i - 1) * dx # Python i → Julia i-1 + end + + # Generate cell center coordinates + for i in 1:ncells + xcc[i] = 0.5 * (x[i] + x[i+1]) + end + + new(xmin, xmax, ncells, nnodes, nx, x, xcc, L, dx) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/julia/reconstructor/weno3.jl b/example/1d-linear-convection/weno3/julia/01h/julia/reconstructor/weno3.jl new file mode 100644 index 00000000..2b6fe1ab --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/julia/reconstructor/weno3.jl @@ -0,0 +1,65 @@ +# julia/reconstructor/weno3.jl +""" +WENO3 重构器(与 reconstructor/weno3.py 完全同构) +""" + +mutable struct Weno3Reconstructor + # 无字段,与 Python 一致 +end + +function reconstruct(rec::Weno3Reconstructor, q::Vector{Float64}, cfd::Any) + domain = cfd.domain + solution = cfd.solution + _reconstruct_left_interfaces(domain, q, solution.q_face_left) + _reconstruct_right_interfaces(domain, q, solution.q_face_right) +end + +function _reconstruct_left_interfaces(domain, u, qL) + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in (domain.ist - 1):(domain.ied - 1) + j = i - (domain.ist - 1) + 1 # ← Julia 1-based: j = i - (ist-1) 对应 Python j = i - (ist-1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = _reconstruct_from_left_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_right_interfaces(domain, u, qR) + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in domain.ist:domain.ied + j = i - domain.ist + 1 # ← Julia 1-based: j = i - ist 对应 Python j = i - ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = _reconstruct_from_right_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_from_left_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -0.5*v1 + 1.5*v2 # r=1 stencil + q1 = 0.5*v2 + 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end + +function _reconstruct_from_right_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 0.5*v1 + 0.5*v2 # r=1 stencil + q1 = 1.5*v2 - 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/julia/residual.jl b/example/1d-linear-convection/weno3/julia/01h/julia/residual.jl new file mode 100644 index 00000000..15cc9387 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/julia/residual.jl @@ -0,0 +1,65 @@ +# julia/residual.jl +""" +残差计算器(与 residual.py 完全同构) +- 封装重建→通量→散度完整流程 +- 依赖 cfd 的多个字段 +""" + +include("mesh.jl") + +mutable struct ResidualCalculator + cfd::Any + config::Any + domain::Any + solution::Any + mesh::Mesh + reconstructor::Any + flux_calculator::Any # 通量计算器(外部传入,替代工厂) + + function ResidualCalculator(cfd::Any, flux_calculator::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + mesh = domain.mesh + reconstructor = cfd.reconstructor + + new(cfd, config, domain, solution, mesh, reconstructor, flux_calculator) + end +end + +""" +计算完整残差(对外唯一接口) +""" +function compute!(calc::ResidualCalculator) + #_reconstruct(calc) + _compute_inviscid_flux(calc) + _compute_flux_divergence(calc) +end + +""" +私有方法:界面值重建 +""" +function _reconstruct(calc::ResidualCalculator) + reconstruct(calc.reconstructor, calc.solution.u, calc.cfd) +end + +""" +私有方法:计算无粘通量 +""" +function _compute_inviscid_flux(calc::ResidualCalculator) + compute!(calc.flux_calculator, + calc.solution.q_face_left, + calc.solution.q_face_right, + calc.solution.flux) +end + +""" +私有方法:计算通量散度(残差 = -dF/dx) +""" +function _compute_flux_divergence(calc::ResidualCalculator) + solution = calc.solution + mesh = calc.mesh + for i in 1:mesh.ncells + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / mesh.dx + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/julia/solution.jl b/example/1d-linear-convection/weno3/julia/01h/julia/solution.jl new file mode 100644 index 00000000..90ca0393 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/julia/solution.jl @@ -0,0 +1,75 @@ +# julia/solution.jl +""" +Solution 结构体(与真实 solution.py 完全同构) +字段顺序、方法、逻辑 1:1 对齐 +""" + +include("mesh.jl") +include("domain.jl") +include("initial_condition.jl") + +mutable struct Solution + domain::Domain + q_face_left::Vector{Float64} + q_face_right::Vector{Float64} + flux::Vector{Float64} + res::Vector{Float64} + u::Vector{Float64} + un::Vector{Float64} + + function Solution(config::Any, domain::Domain) + mesh = domain.mesh + + q_face_left = zeros(Float64, mesh.nnodes) + q_face_right = zeros(Float64, mesh.nnodes) + flux = zeros(Float64, mesh.nnodes) + res = zeros(Float64, mesh.ncells) + u = zeros(Float64, domain.ntcells) + un = zeros(Float64, domain.ntcells) + + sol = new(domain, q_face_left, q_face_right, flux, res, u, un) + initialize_from_config(sol, config) + return sol + end +end + +""" +重置解数组为初始状态 +""" +function reset_solution(sol::Solution) + fill!(sol.u, 0.0) + fill!(sol.un, 0.0) +end + +""" +根据配置初始化场 +注:暂用硬编码替代 InitialConditionFactory +""" +function initialize_from_config(sol::Solution, config::Any) + ic_type = getfield_safe(config, :ic_type, "step") + + # 硬编码创建 IC(替代 InitialConditionFactory) + ic = if ic_type == "step" + StepFunctionIC(config) + elseif ic_type == "sin" + SineWaveIC(config) + elseif ic_type == "gaussian" + GaussianPulseIC(config) + else + error("未知初始条件类型: $ic_type") + end + + apply(ic, sol) # 调用 IC.apply +end + +""" +更新旧场:un = u +""" +function update_old_field(sol::Solution) + sol.un .= sol.u # 等价于 Python 的 un[:] = u[:] +end + +# ---------------------- 辅助函数 ---------------------- +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/julia/test/test_boundary.jl b/example/1d-linear-convection/weno3/julia/01h/julia/test/test_boundary.jl new file mode 100644 index 00000000..ef27d682 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/julia/test/test_boundary.jl @@ -0,0 +1,57 @@ +# julia/test/test_boundary.jl +using NPZ +include("../boundary.jl") + +struct MockConfig + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + debug::Bool +end + +struct MockDomain + nghosts::Int + ist::Int # Julia 本地索引(1-based) + ied::Int + ntcells::Int +end + +struct MockCfd + config::MockConfig + domain::MockDomain +end + +# ===== 关键:使用 Julia 本地索引规则 ===== +nghosts = 2 +ncells = 40 +ist = nghosts + 1 # = 3 +ied = ist + ncells # = 43 +ntcells = ncells + 2 * nghosts # = 44 + +config = MockConfig("dirichlet", 0.5, 1.5, true) +domain = MockDomain(nghosts, ist, ied, ntcells) +cfd_mock = MockCfd(config, domain) + +# 加载 Python 生成的 u_input.npy +# 注意:Python u[0] → Julia u[1],所以内容完全对应 +u_input = npzread("../../python/u_input.npy") +@assert length(u_input) == ntcells "数组长度不匹配!" + +# 测试三种边界 +test_cases = [ + ("periodic", PeriodicBoundary(cfd_mock)), + ("dirichlet", DirichletBoundary(cfd_mock)), + ("neumann", NeumannBoundary(cfd_mock)) +] + +for (name, bc) in test_cases + u = copy(u_input) + apply!(bc, u) + + u_py = npzread("../../python/u_$(name)_py.npy") + err = maximum(abs.(u .- u_py)) + println("边界: $(name) | 最大误差: $(err)") + @assert err < 1e-12 "❌ $(name) 不一致!" +end + +println("✅ 所有边界条件测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/julia/test/test_domain.jl b/example/1d-linear-convection/weno3/julia/01h/julia/test/test_domain.jl new file mode 100644 index 00000000..7b423f77 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/julia/test/test_domain.jl @@ -0,0 +1,44 @@ +# julia/test/test_domain.jl +include("../mesh.jl") +include("../domain.jl") + +# MockConfig:模拟 Python CfdConfig +struct MockConfig + recon_scheme::String + spatial_order::Int +end + +# 测试 ENO +config_eno = MockConfig("eno", 2) +mesh = Mesh() +domain_eno = Domain(config_eno, mesh) + +println("ENO: nghosts = ", domain_eno.nghosts) # 2 +println("ENO: ist = ", domain_eno.ist) # 2 +println("ENO: ied = ", domain_eno.ied) # 42 +println("物理索引范围: ", collect(get_physical_indices(domain_eno))[1:3], " ... ", collect(get_physical_indices(domain_eno))[end-2:end]) + +# 测试 WENO(字符串 "weno") +config_weno = MockConfig("weno", 2) +domain_weno = Domain(config_weno, mesh) +println("WENO: nghosts = ", domain_weno.nghosts) # 2 + +# 测试 WENO3(字符串 "weno3") +config_weno3 = MockConfig("weno3", 2) +domain_weno3 = Domain(config_weno3, mesh) +println("WENO3: nghosts = ", domain_weno3.nghosts) # 2 + +# 测试 is_physical_cell +@assert is_physical_cell(domain_eno, 2) == true # ist=2 +@assert is_physical_cell(domain_eno, 41) == true # ied-1=41 +@assert is_physical_cell(domain_eno, 42) == false # ied=42 + +# ✅ 断言 +@assert domain_eno.nghosts == 2 +@assert domain_eno.ist == 2 +@assert domain_eno.ied == 42 +@assert domain_eno.ntcells == 44 +@assert domain_weno.nghosts == 2 +@assert domain_weno3.nghosts == 2 + +println("✅ Domain 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/julia/test/test_flux.jl b/example/1d-linear-convection/weno3/julia/01h/julia/test/test_flux.jl new file mode 100644 index 00000000..0ebc1dc0 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/julia/test/test_flux.jl @@ -0,0 +1,53 @@ +# julia/test/test_flux.jl +include("../mesh.jl") +include("../flux.jl") + +# Mock 对象 +struct MockConfig + flux_type::String + wave_speed::Float64 +end + +struct MockDomain + mesh::Mesh +end + +struct MockCfd + config::MockConfig + domain::MockDomain +end + +# 创建 mock +mesh = Mesh() +config = MockConfig("rusanov", 1.0) +domain = MockDomain(mesh) +cfd = MockCfd(config, domain) + +# 测试 Rusanov +rusanov = RusanovFluxCalculator(cfd) +N = mesh.nnodes +qL = [1.0, 2.0, 3.0, 4.0, 5.0, zeros(Float64, N-5)...] +qR = [2.0, 3.0, 4.0, 5.0, 6.0, zeros(Float64, N-5)...] +flux_rus = zeros(Float64, N) +compute!(rusanov, qL, qR, flux_rus) + +println("Rusanov flux[1] = ", flux_rus[1]) # 应为 1.0 +@assert abs(flux_rus[1] - 1.0) < 1e-12 + +# 测试 Engquist-Osher +eo = EngquistOsherFluxCalculator(cfd) +flux_eo = zeros(Float64, N) +compute!(eo, qL, qR, flux_eo) + +println("EO flux[1] = ", flux_eo[1]) # c=1 → cp=1, cm=0 → flux=1*1 + 0*2 = 1.0 +@assert abs(flux_eo[1] - 1.0) < 1e-12 + +# 测试 c = -1.0 +config_neg = MockConfig("rusanov", -1.0) +cfd_neg = MockCfd(config_neg, domain) +rusanov_neg = RusanovFluxCalculator(cfd_neg) +compute!(rusanov_neg, qL, qR, flux_rus) +println("Rusanov (c=-1) flux[1] = ", flux_rus[1]) # F_L=-1, F_R=-2, Smax=1 → flux = -1.5 -0.5*(-1) = -1.0 +@assert abs(flux_rus[1] - (-2.0)) < 1e-12 # ← 修正:-2.0 而非 -1.0 + +println("✅ Flux 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/julia/test/test_initial_condition.jl b/example/1d-linear-convection/weno3/julia/01h/julia/test/test_initial_condition.jl new file mode 100644 index 00000000..dc58691a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/julia/test/test_initial_condition.jl @@ -0,0 +1,45 @@ +# julia/test/test_initial_condition.jl +using NPZ + +# 包含初始条件模块 +include("../initial_condition.jl") + +# 构造 mock config(与 Python 完全一致) +struct MockConfig + ic_type::String + domain_length::Float64 + pulse_center::Float64 + pulse_width::Float64 +end + +# 生成与 Python Mesh.xcc 完全相同的 x 坐标 +function generate_xcc() + xmin, xmax = 0.0, 2.0 + ncells = 40 + dx = (xmax - xmin) / ncells + xcc = Vector{Float64}(undef, ncells) + for i in 1:ncells + xcc[i] = xmin + (i - 0.5) * dx # i-1 + 0.5 → i-0.5 + end + return xcc +end + +# 主测试 +xcc = generate_xcc() + +test_cases = [ + ("step", StepFunctionIC(MockConfig("step", 2.0, 0.5, 0.1))), + ("sin", SineWaveIC(MockConfig("sin", 2.0, 0.5, 0.1))), + ("gaussian", GaussianPulseIC(MockConfig("gaussian", 2.0, 0.5, 0.1))) +] + +for (name, ic) in test_cases + u_jl = evaluate_at(ic, xcc) + u_py = npzread("../../python/u_$(name)_interior_py.npy") + + err = maximum(abs.(u_jl .- u_py)) + println("IC: $(name) | 最大误差: $(err)") + @assert err < 1e-12 "❌ $(name) 不一致!" +end + +println("✅ 所有初始条件测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/julia/test/test_mesh.jl b/example/1d-linear-convection/weno3/julia/01h/julia/test/test_mesh.jl new file mode 100644 index 00000000..315d2aac --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/julia/test/test_mesh.jl @@ -0,0 +1,37 @@ +# julia/test/test_mesh.jl +include("../mesh.jl") + +# 创建 mesh(与 Python 完全相同) +mesh = Mesh() + +# 打印关键值(与 Python 对比) +println("xmin = ", mesh.xmin) # 0.0 +println("xmax = ", mesh.xmax) # 2.0 +println("ncells = ", mesh.ncells) # 40 +println("nnodes = ", mesh.nnodes) # 41 +println("nx = ", mesh.nx) # 40 +println("L = ", mesh.L) # 2.0 +println("dx = ", mesh.dx) # 0.05 + +# 检查 x[1] (Python x[0]) 和 x[41] (Python x[40]) +println("x[1] = ", mesh.x[1]) # 0.0 +println("x[41] = ", mesh.x[41]) # 2.0 + +# 检查 xcc[1] (Python xcc[0]) 和 xcc[40] (Python xcc[39]) +println("xcc[1] = ", mesh.xcc[1]) # 0.025 +println("xcc[40] = ", mesh.xcc[40]) # 1.975 + +# ✅ 严格断言 +@assert mesh.xmin == 0.0 +@assert mesh.xmax == 2.0 +@assert mesh.ncells == 40 +@assert mesh.nnodes == 41 +@assert mesh.nx == 40 +@assert mesh.L == 2.0 +@assert mesh.dx == 0.05 +@assert mesh.x[1] == 0.0 +@assert mesh.x[41] == 2.0 +@assert abs(mesh.xcc[1] - 0.025) < 1e-12 +@assert abs(mesh.xcc[40] - 1.975) < 1e-12 + +println("✅ Mesh 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/julia/test/test_residual.jl b/example/1d-linear-convection/weno3/julia/01h/julia/test/test_residual.jl new file mode 100644 index 00000000..e37e8d2b --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/julia/test/test_residual.jl @@ -0,0 +1,53 @@ +# julia/test/test_residual.jl +include("../mesh.jl") +include("../domain.jl") +include("../solution.jl") +include("../flux.jl") +include("../residual.jl") + +# ===== Dummy Reconstructor ===== +struct DummyReconstructor end + +# ===== Mock Config ===== +struct MockConfig + flux_type::String + wave_speed::Float64 +end + +# ===== Mock CFD (必须包含 reconstructor 字段) ===== +struct MockCfd + config::MockConfig + domain::Domain + solution::Solution + reconstructor::DummyReconstructor # ← 关键:添加此字段 +end + +# ===== 主测试 ===== +config = MockConfig("rusanov", 1.0) +mesh = Mesh() +domain = Domain((recon_scheme="eno", spatial_order=2), mesh) +solution = Solution((ic_type="step",), domain) + +# 手动设置界面值(跳过重建) +for i in 1:mesh.nnodes + solution.q_face_left[i] = Float64(i) * 0.1 + solution.q_face_right[i] = Float64(i) * 0.1 +end + +flux_calc = RusanovFluxCalculator((config=config, domain=domain)) +dummy_recon = DummyReconstructor() +cfd = MockCfd(config, domain, solution, dummy_recon) + +# 创建残差计算器 +res_calc = ResidualCalculator(cfd, flux_calc) + +# 计算残差(注意:_reconstruct 仍被注释) +compute!(res_calc) + +println("flux[1] = ", solution.flux[1]) +println("res[1] = ", solution.res[1]) + +@assert abs(solution.flux[1] - 0.1) < 1e-12 +@assert abs(solution.res[1] - (-2.0)) < 1e-12 + +println("✅ Residual 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/julia/test/test_solution.jl b/example/1d-linear-convection/weno3/julia/01h/julia/test/test_solution.jl new file mode 100644 index 00000000..f44ff0f0 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/julia/test/test_solution.jl @@ -0,0 +1,42 @@ +# julia/test/test_solution.jl +include("../mesh.jl") +include("../domain.jl") +include("../solution.jl") + +# MockConfig +struct MockConfig + recon_scheme::String + spatial_order::Int + ic_type::String + domain_length::Float64 + pulse_center::Float64 + pulse_width::Float64 +end + +# 创建 solution +config = MockConfig("eno", 2, "step", 2.0, 0.5, 0.1) +mesh = Mesh() +domain = Domain(config, mesh) +sol = Solution(config, domain) + +# 检查字段尺寸 +@assert length(sol.q_face_left) == mesh.nnodes # 41 +@assert length(sol.flux) == mesh.nnodes # 41 +@assert length(sol.res) == mesh.ncells # 40 +@assert length(sol.u) == domain.ntcells # 44 + +# 检查初始场 +println("u[3] (物理起始): ", sol.u[3]) # 应为 1.0 +println("u[23] (x=1.0): ", sol.u[23]) # 应为 2.0 + +# 测试 update_old_field +sol.u[3] = 999.0 +update_old_field(sol) +@assert sol.un[3] == 999.0 + +# 测试 reset_solution +reset_solution(sol) +@assert sol.u[3] == 0.0 +@assert sol.un[3] == 0.0 + +println("✅ Solution 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/julia/test/test_time_integration.jl b/example/1d-linear-convection/weno3/julia/01h/julia/test/test_time_integration.jl new file mode 100644 index 00000000..091a8637 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/julia/test/test_time_integration.jl @@ -0,0 +1,53 @@ +# julia/test/test_time_integration.jl +include("../time_integration.jl") +include("../flux.jl") +include("../boundary.jl") + +# Mock CFD 对象 +struct MockCfd + config::Any + domain::Domain + solution::Solution + residual_calculator::Any + boundary_condition::Any +end + +# 创建完整链路 +mesh = Mesh() +domain = Domain((recon_scheme="eno", spatial_order=2), mesh) +solution = Solution((ic_type="step",), domain) + +# 手动设置界面值(跳过 reconstructor) +for i in 1:mesh.nnodes + solution.q_face_left[i] = Float64(i) * 0.1 + solution.q_face_right[i] = Float64(i) * 0.1 +end + +# Mock components +flux_calc = RusanovFluxCalculator((config=(wave_speed=1.0,), domain=domain)) +res_calc = ResidualCalculator((config=nothing, domain=domain, solution=solution, reconstructor=nothing), flux_calc) +bc = PeriodicBoundary((domain=domain, config=(debug=false,))) +cfd = MockCfd((rk_order=1,), domain, solution, res_calc, bc) + +# 测试 RK1 +rk1 = RK1Integrator(cfd) +step(rk1, 0.025) + +println("RK1 step completed") +@assert solution.u[3] != 0.0 # 应已更新 + +# 测试 RK2 +cfd2 = MockCfd((rk_order=2,), domain, solution, res_calc, bc) +rk2 = RK2Integrator(cfd2) +step(rk2, 0.025) + +println("RK2 step completed") + +# 测试 RK3 +cfd3 = MockCfd((rk_order=3,), domain, solution, res_calc, bc) +rk3 = RK3Integrator(cfd3) +step(rk3, 0.025) + +println("RK3 step completed") + +println("✅ Time Integration 逻辑测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/julia/test/test_weno3.jl b/example/1d-linear-convection/weno3/julia/01h/julia/test/test_weno3.jl new file mode 100644 index 00000000..9a91761e --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/julia/test/test_weno3.jl @@ -0,0 +1,47 @@ +# julia/test/test_weno3.jl +include("../mesh.jl") +include("../domain.jl") +include("../solution.jl") +include("../reconstructor/weno3.jl") + +# Mock CFD +struct MockCfd + domain::Domain + solution::Solution +end + +# 创建数据 +mesh = Mesh() +# 注意:Domain 现在使用 1-based 索引(ist = nghosts + 1) +domain = Domain((recon_scheme="weno3", spatial_order=2), mesh) +solution = Solution((ic_type="step",), domain) + +# 设置 u(物理区域 + ghost) +u = solution.u +for i in 1:domain.ntcells + u[i] = Float64(i-1) * 0.1 # u = [0.0, 0.1, 0.2, ..., 4.3] +end + +# 创建 reconstructor +weno3 = Weno3Reconstructor() +cfd = MockCfd(domain, solution) + +# 重建 +reconstruct(weno3, u, cfd) + +println("q_face_left length = ", length(solution.q_face_left)) # 应为 41 +println("q_face_right length = ", length(solution.q_face_right)) # 应为 41 +println("q_face_left[1] = ", solution.q_face_left[1]) +println("q_face_right[1] = ", solution.q_face_right[1]) + +# 验证左界面值(i = ist-1 = 2, j = 1) +# v1 = u[1] = 0.0, v2 = u[2] = 0.1, v3 = u[3] = 0.2 +# qL = w0*(-0.5*0.0 + 1.5*0.1) + w1*(0.5*0.1 + 0.5*0.2) = w0*0.15 + w1*0.15 = 0.15 +@assert abs(solution.q_face_left[1] - 0.15) < 1e-12 + +# 验证右界面值(i = ist = 3, j = 1) +# v1 = u[2] = 0.1, v2 = u[3] = 0.2, v3 = u[4] = 0.3 +# qR = w0*(0.5*0.1 + 0.5*0.2) + w1*(1.5*0.2 - 0.5*0.3) = w0*0.15 + w1*0.15 = 0.15 +@assert abs(solution.q_face_right[1] - 0.15) < 1e-12 + +println("✅ WENO3 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/julia/test_residual.jl b/example/1d-linear-convection/weno3/julia/01h/julia/test_residual.jl new file mode 100644 index 00000000..315d2aac --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/julia/test_residual.jl @@ -0,0 +1,37 @@ +# julia/test/test_mesh.jl +include("../mesh.jl") + +# 创建 mesh(与 Python 完全相同) +mesh = Mesh() + +# 打印关键值(与 Python 对比) +println("xmin = ", mesh.xmin) # 0.0 +println("xmax = ", mesh.xmax) # 2.0 +println("ncells = ", mesh.ncells) # 40 +println("nnodes = ", mesh.nnodes) # 41 +println("nx = ", mesh.nx) # 40 +println("L = ", mesh.L) # 2.0 +println("dx = ", mesh.dx) # 0.05 + +# 检查 x[1] (Python x[0]) 和 x[41] (Python x[40]) +println("x[1] = ", mesh.x[1]) # 0.0 +println("x[41] = ", mesh.x[41]) # 2.0 + +# 检查 xcc[1] (Python xcc[0]) 和 xcc[40] (Python xcc[39]) +println("xcc[1] = ", mesh.xcc[1]) # 0.025 +println("xcc[40] = ", mesh.xcc[40]) # 1.975 + +# ✅ 严格断言 +@assert mesh.xmin == 0.0 +@assert mesh.xmax == 2.0 +@assert mesh.ncells == 40 +@assert mesh.nnodes == 41 +@assert mesh.nx == 40 +@assert mesh.L == 2.0 +@assert mesh.dx == 0.05 +@assert mesh.x[1] == 0.0 +@assert mesh.x[41] == 2.0 +@assert abs(mesh.xcc[1] - 0.025) < 1e-12 +@assert abs(mesh.xcc[40] - 1.975) < 1e-12 + +println("✅ Mesh 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/julia/time_integration.jl b/example/1d-linear-convection/weno3/julia/01h/julia/time_integration.jl new file mode 100644 index 00000000..95e5cd67 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/julia/time_integration.jl @@ -0,0 +1,130 @@ +# julia/time_integration.jl +""" +时间推进器模块(与 time_integration.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("residual.jl") +include("boundary.jl") + +# ---------------------- 抽象时间推进器基类 ---------------------- +abstract type TimeIntegrator end + +mutable struct TimeIntegratorBase <: TimeIntegrator + cfd::Any + config::Any + domain::Domain + solution::Solution + residual_calculator::Any # ResidualCalculator +end + +function TimeIntegratorBase(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + residual_calculator = cfd.residual_calculator + TimeIntegratorBase(cfd, config, domain, solution, residual_calculator) +end + +function compute_residual(integrator::TimeIntegratorBase) + compute!(integrator.residual_calculator) +end + +function apply_boundary(integrator::TimeIntegratorBase) + apply!(integrator.cfd.boundary_condition, integrator.solution.u) +end + +function map_idx(integrator::TimeIntegratorBase, i::Int) + return i - integrator.domain.ist + 1 # ← +1 转为 1-based +end + +# ---------------------- RK1Integrator ---------------------- +mutable struct RK1Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK1Integrator(cfd::Any) + RK1Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK1Integrator, dt::Float64) + compute_residual(integrator.base) + for i in integrator.base.domain.ist:(integrator.base.domain.ied - 1) + j = map_idx(integrator.base, i) + integrator.base.solution.u[i] += dt * integrator.base.solution.res[j] + end + apply_boundary(integrator.base) + update_old_field(integrator.base.solution) +end + +# ---------------------- RK2Integrator ---------------------- +mutable struct RK2Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK2Integrator(cfd::Any) + RK2Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK2Integrator, dt::Float64) + base = integrator.base + # 阶段1:预测步 + compute_residual(base) + u_pred = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u_pred[i] += dt * base.solution.res[j] + end + base.solution.u .= u_pred + apply_boundary(base) + # 阶段2:校正步 + compute_residual(base) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = 0.5 * base.solution.un[i] + 0.5 * base.solution.u[i] + 0.5 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- RK3Integrator ---------------------- +mutable struct RK3Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK3Integrator(cfd::Any) + RK3Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK3Integrator, dt::Float64) + base = integrator.base + # 阶段1 + compute_residual(base) + u1 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u1[i] += dt * base.solution.res[j] + end + base.solution.u .= u1 + apply_boundary(base) + # 阶段2 + compute_residual(base) + u2 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u2[i] = 0.75 * base.solution.un[i] + 0.25 * base.solution.u[i] + 0.25 * dt * base.solution.res[j] + end + base.solution.u .= u2 + apply_boundary(base) + # 阶段3 + compute_residual(base) + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = c1 * base.solution.un[i] + c2 * base.solution.u[i] + c3 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/python/boundary.py b/example/1d-linear-convection/weno3/julia/01h/python/boundary.py new file mode 100644 index 00000000..6054f92d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/python/boundary.py @@ -0,0 +1,103 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/python/cfd_registry.py b/example/1d-linear-convection/weno3/julia/01h/python/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/python/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/python/config.py b/example/1d-linear-convection/weno3/julia/01h/python/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/python/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/python/domain.py b/example/1d-linear-convection/weno3/julia/01h/python/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/python/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/python/factories/base_factory.py b/example/1d-linear-convection/weno3/julia/01h/python/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/python/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/python/flux.py b/example/1d-linear-convection/weno3/julia/01h/python/flux.py new file mode 100644 index 00000000..5ac73aa8 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/python/flux.py @@ -0,0 +1,74 @@ +# flux.py +""" +通量计算器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册,替代硬编码工厂 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象通量计算基类(统一接口) ---------------------- +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass + +# ---------------------- 2. 具体通量计算子类(使用装饰器注册) ---------------------- + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + +class FluxCalculatorFactory: + """通量计算器工厂""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD对象 + + Returns: + 通量计算器实例 + """ + from factories.base_factory import BaseFactory + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/python/gen_boundary_test_data.py b/example/1d-linear-convection/weno3/julia/01h/python/gen_boundary_test_data.py new file mode 100644 index 00000000..c7fc2a9c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/python/gen_boundary_test_data.py @@ -0,0 +1,35 @@ +# python/gen_boundary_test_data.py +import numpy as np +from config import CfdConfig +from mesh import Mesh +from domain import Domain + +# 固定测试配置 +config = CfdConfig() +config.with_boundary("dirichlet", left_value=0.5, right_value=1.5) +config.debug = True + +mesh = Mesh() +domain = Domain(config, mesh) + +# 构造 mock CFD 对象(仅含 config + domain) +class MockCfd: + def __init__(self, config, domain): + self.config = config + self.domain = domain + +# 测试用 u:0,1,2,...,N-1 +u_input = np.arange(domain.ntcells, dtype=np.float64) +np.save("u_input.npy", u_input) + +# 测试每种边界 +from boundary import PeriodicBoundary, DirichletBoundary, NeumannBoundary + +for bc_name, bc_class in [("periodic", PeriodicBoundary), ("dirichlet", DirichletBoundary), ("neumann", NeumannBoundary)]: + u = u_input.copy() + cfd_mock = MockCfd(config, domain) + bc = bc_class(cfd_mock) + bc.apply(u) + np.save(f"u_{bc_name}_py.npy", u) + +print("✅ 测试数据已生成:u_*.npy") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/python/gen_ic_test_data.py b/example/1d-linear-convection/weno3/julia/01h/python/gen_ic_test_data.py new file mode 100644 index 00000000..69c2573b --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/python/gen_ic_test_data.py @@ -0,0 +1,27 @@ +# python/gen_ic_test_data.py +import sys, os +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +import numpy as np +from config import CfdConfig +from mesh import Mesh +from domain import Domain +from solution import Solution + +# 固定 mesh +mesh = Mesh() +config = CfdConfig() + +# 测试三种 IC +for ic_type in ["step", "sin", "gaussian"]: + config.ic_type = ic_type + domain = Domain(config, mesh) + sol = Solution(config, domain) + + u_full = sol.u.copy() # 包含 ghost + u_interior = sol.u[domain.ist:domain.ied].copy() + + np.save(f"u_{ic_type}_full_py.npy", u_full) + np.save(f"u_{ic_type}_interior_py.npy", u_interior) + +print("✅ 初始条件测试数据已生成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/python/initial_condition.py b/example/1d-linear-convection/weno3/julia/01h/python/initial_condition.py new file mode 100644 index 00000000..047415b7 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/python/initial_condition.py @@ -0,0 +1,90 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, ic_type: str, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', ic_type, config) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/python/mesh.py b/example/1d-linear-convection/weno3/julia/01h/python/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/python/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/python/plotter.py b/example/1d-linear-convection/weno3/julia/01h/python/plotter.py new file mode 100644 index 00000000..9f1a414f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/python/plotter.py @@ -0,0 +1,107 @@ +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/python/reconstructor/__init__.py b/example/1d-linear-convection/weno3/julia/01h/python/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/python/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/python/reconstructor/base.py b/example/1d-linear-convection/weno3/julia/01h/python/reconstructor/base.py new file mode 100644 index 00000000..bbd63850 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/python/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def reconstruct(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/python/reconstructor/eno.py b/example/1d-linear-convection/weno3/julia/01h/python/reconstructor/eno.py new file mode 100644 index 00000000..c2fb385d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/python/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def reconstruct(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/python/reconstructor/factory.py b/example/1d-linear-convection/weno3/julia/01h/python/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/python/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/python/reconstructor/weno3.py b/example/1d-linear-convection/weno3/julia/01h/python/reconstructor/weno3.py new file mode 100644 index 00000000..bf68be50 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/python/reconstructor/weno3.py @@ -0,0 +1,59 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def reconstruct(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/julia/01h/python/registry.py b/example/1d-linear-convection/weno3/julia/01h/python/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/python/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/python/residual.py b/example/1d-linear-convection/weno3/julia/01h/python/residual.py new file mode 100644 index 00000000..b4d4d7dc --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/python/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._reconstruct() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _reconstruct(self): + """私有方法:界面值重建""" + self.reconstructor.reconstruct(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/python/run_eno_weno.py b/example/1d-linear-convection/weno3/julia/01h/python/run_eno_weno.py new file mode 100644 index 00000000..fd7f3874 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/python/run_eno_weno.py @@ -0,0 +1,52 @@ +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/python/solution.py b/example/1d-linear-convection/weno3/julia/01h/python/solution.py new file mode 100644 index 00000000..92a46d3f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/python/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config.ic_type, config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/python/solver.py b/example/1d-linear-convection/weno3/julia/01h/python/solver.py new file mode 100644 index 00000000..0d0b442d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/python/solver.py @@ -0,0 +1,86 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + + +from flux import InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory + +from boundary import BoundaryCondition, PeriodicBoundary, DirichletBoundary, NeumannBoundary, BoundaryConditionFactory + +from time_integration import TimeIntegrator, RK1Integrator, RK2Integrator, RK3Integrator, TimeIntegratorFactory + +from mesh import Mesh + +from reconstructor import ReconstructorFactory + +from initial_condition import InitialCondition, StepFunctionIC, SineWaveIC, GaussianPulseIC, InitialConditionFactory + +from domain import Domain +from solution import Solution + +from config import CfdConfig +from residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config.ic_type, self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + #runge_kutta(self) + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/julia/01h/python/time_integration.py b/example/1d-linear-convection/weno3/julia/01h/python/time_integration.py new file mode 100644 index 00000000..25ac0b4c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01h/python/time_integration.py @@ -0,0 +1,125 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class TimeIntegratorFactory: + """时间推进器工厂""" + + @staticmethod + def create(cfd) -> 'TimeIntegrator': + """ + 创建时间推进器实例 + + Args: + cfd: CFD对象 + + Returns: + 时间推进器实例 + """ + from factories.base_factory import BaseFactory + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01h/python/u_dirichlet_py.npy b/example/1d-linear-convection/weno3/julia/01h/python/u_dirichlet_py.npy new file mode 100644 index 00000000..3aa20bd2 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01h/python/u_dirichlet_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01h/python/u_gaussian_full_py.npy b/example/1d-linear-convection/weno3/julia/01h/python/u_gaussian_full_py.npy new file mode 100644 index 00000000..b762f0fe Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01h/python/u_gaussian_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01h/python/u_gaussian_interior_py.npy b/example/1d-linear-convection/weno3/julia/01h/python/u_gaussian_interior_py.npy new file mode 100644 index 00000000..68af8954 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01h/python/u_gaussian_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01h/python/u_input.npy b/example/1d-linear-convection/weno3/julia/01h/python/u_input.npy new file mode 100644 index 00000000..ef506fa7 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01h/python/u_input.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01h/python/u_neumann_py.npy b/example/1d-linear-convection/weno3/julia/01h/python/u_neumann_py.npy new file mode 100644 index 00000000..fa723d1e Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01h/python/u_neumann_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01h/python/u_periodic_py.npy b/example/1d-linear-convection/weno3/julia/01h/python/u_periodic_py.npy new file mode 100644 index 00000000..156aff85 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01h/python/u_periodic_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01h/python/u_sin_full_py.npy b/example/1d-linear-convection/weno3/julia/01h/python/u_sin_full_py.npy new file mode 100644 index 00000000..b87f8a13 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01h/python/u_sin_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01h/python/u_sin_interior_py.npy b/example/1d-linear-convection/weno3/julia/01h/python/u_sin_interior_py.npy new file mode 100644 index 00000000..6803fbfb Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01h/python/u_sin_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01h/python/u_step_full_py.npy b/example/1d-linear-convection/weno3/julia/01h/python/u_step_full_py.npy new file mode 100644 index 00000000..2fc0e183 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01h/python/u_step_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01h/python/u_step_interior_py.npy b/example/1d-linear-convection/weno3/julia/01h/python/u_step_interior_py.npy new file mode 100644 index 00000000..b5ad6600 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01h/python/u_step_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01i/julia/boundary.jl b/example/1d-linear-convection/weno3/julia/01i/julia/boundary.jl new file mode 100644 index 00000000..5b0baaf4 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/julia/boundary.jl @@ -0,0 +1,90 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/julia/domain.jl b/example/1d-linear-convection/weno3/julia/01i/julia/domain.jl new file mode 100644 index 00000000..a7edc226 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/julia/domain.jl @@ -0,0 +1,61 @@ +# julia/domain.jl +""" +Domain 结构体(与 domain.py 完全同构) +- 保存 config +- nghosts 计算逻辑 1:1 +- ist/ied 为 0-based(与 Python 一致) +""" + +include("mesh.jl") + +mutable struct Domain + config::Any # 保存 config(与 Python 一致) + mesh::Mesh + nghosts::Int + ist::Int # 0-based + ied::Int # 0-based + ntcells::Int +end + +function Domain(config::Any, mesh::Mesh) + nghosts = _calc_nghosts(config) + ist = nghosts + 1 # ← 1-based 起始索引 + ied = ist + mesh.ncells # ← 1-based 结束索引(不包含) + ntcells = mesh.ncells + 2 * nghosts + Domain(config, mesh, nghosts, ist, ied, ntcells) +end + +function _calc_nghosts(config::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + order = getfield_safe(config, :spatial_order, 2) + + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + if scheme == "eno" + nghosts = order + elseif startswith(scheme, "weno") + nghosts = order ÷ 2 + 1 + else + error("未知重建格式 $(scheme),无法计算ghost层!") + end + + if nghosts <= 0 + error("计算得到的ghost层数量无效:$(nghosts)(阶数$(order),格式$(scheme))") + end + + return nghosts +end + +function is_physical_cell(domain::Domain, idx::Int) + return domain.ist <= idx < domain.ied +end + +function get_physical_indices(domain::Domain) + return domain.ist:(domain.ied - 1) +end + +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/julia/flux.jl b/example/1d-linear-convection/weno3/julia/01i/julia/flux.jl new file mode 100644 index 00000000..047598f7 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/julia/flux.jl @@ -0,0 +1,70 @@ +# julia/flux.jl +""" +通量计算器模块(与 flux.py 完全同构) +- 抽象基类 + 具体实现 +- 字段:cfd, config, mesh, wave_speed +""" + +include("mesh.jl") + +# ---------------------- 抽象基类 ---------------------- +""" +InviscidFluxCalculator 抽象类型 +Julia 无 ABC,用文档约定 +所有子类型必须实现 compute! +""" +abstract type InviscidFluxCalculator end + +# ---------------------- RusanovFluxCalculator ---------------------- +mutable struct RusanovFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function RusanovFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::RusanovFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = calc.wave_speed + c_R = calc.wave_speed + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + end +end + +# ---------------------- EngquistOsherFluxCalculator ---------------------- +mutable struct EngquistOsherFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function EngquistOsherFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::EngquistOsherFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + c = calc.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/julia/initial_condition.jl b/example/1d-linear-convection/weno3/julia/01i/julia/initial_condition.jl new file mode 100644 index 00000000..5e029e8d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/julia/initial_condition.jl @@ -0,0 +1,86 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/julia/mesh.jl b/example/1d-linear-convection/weno3/julia/01i/julia/mesh.jl new file mode 100644 index 00000000..1b0dd4bf --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/julia/mesh.jl @@ -0,0 +1,45 @@ +# julia/mesh.jl +""" +Mesh 结构体(与提供的 mesh.py 完全同构) +- 字段名、初始化顺序、循环逻辑完全一致 +- 不做任何泛化或优化 +""" + +mutable struct Mesh + xmin::Float64 + xmax::Float64 + ncells::Int + nnodes::Int + nx::Int + x::Vector{Float64} + xcc::Vector{Float64} + L::Float64 # 在 init_mesh 中赋值 + dx::Float64 # 在 init_mesh 中赋值 + + function Mesh() + # 与 Python __init__ 完全一致 + xmin = 0.0 + xmax = 2.0 + ncells = 40 + nnodes = ncells + 1 + nx = ncells + x = zeros(Float64, nnodes) + xcc = zeros(Float64, ncells) + + # 调用 init_mesh(模拟 Python) + L = xmax - xmin + dx = L / ncells + + # Generate node coordinates: for i in range(self.nnodes) + for i in 1:nnodes + x[i] = xmin + (i - 1) * dx # Python i → Julia i-1 + end + + # Generate cell center coordinates + for i in 1:ncells + xcc[i] = 0.5 * (x[i] + x[i+1]) + end + + new(xmin, xmax, ncells, nnodes, nx, x, xcc, L, dx) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/julia/reconstructor/eno.jl b/example/1d-linear-convection/weno3/julia/01i/julia/reconstructor/eno.jl new file mode 100644 index 00000000..e78a636f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/julia/reconstructor/eno.jl @@ -0,0 +1,107 @@ +# julia/reconstructor/eno.jl +""" +ENO 重构器(与 reconstructor/eno.py 完全同构) +""" + +# ---------------------- ENO 系数初始化 ---------------------- +function _init_eno_coef!(spatial_order::Int, coef::Matrix{Float64}) + if spatial_order == 1 + coef[1, 1] = 1.0 + coef[2, 1] = 1.0 + elseif spatial_order == 2 + coef[1, 1:2] = [3.0/2.0, -1.0/2.0] + coef[2, 1:2] = [1.0/2.0, 1.0/2.0] + coef[3, 1:2] = [-1.0/2.0, 3.0/2.0] + elseif spatial_order == 3 + coef[1, 1:3] = [11.0/6.0, -7.0/6.0, 1.0/3.0] + coef[2, 1:3] = [1.0/3.0, 5.0/6.0, -1.0/6.0] + coef[3, 1:3] = [-1.0/6.0, 5.0/6.0, 1.0/3.0] + coef[4, 1:3] = [1.0/3.0, -7.0/6.0, 11.0/6.0] + elseif spatial_order == 4 + coef[1, 1:4] = [25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0] + coef[2, 1:4] = [1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0] + coef[3, 1:4] = [-1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0] + coef[4, 1:4] = [1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0] + coef[5, 1:4] = [-1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0] + elseif spatial_order == 5 + coef[1, 1:5] = [137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0] + coef[2, 1:5] = [1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0] + coef[3, 1:5] = [-1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0] + coef[4, 1:5] = [1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0] + coef[5, 1:5] = [-1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0] + coef[6, 1:5] = [1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0] + elseif spatial_order == 6 + coef[1, 1:6] = [49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0] + coef[2, 1:6] = [1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0] + coef[3, 1:6] = [-1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0] + coef[4, 1:6] = [1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0] + coef[5, 1:6] = [-1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0] + coef[6, 1:6] = [1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0] + coef[7, 1:6] = [-1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0] + elseif spatial_order == 7 + coef[1, 1:7] = [363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0] + coef[2, 1:7] = [1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0] + coef[3, 1:7] = [-1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0] + coef[4, 1:7] = [1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0] + coef[5, 1:7] = [-1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0] + coef[6, 1:7] = [1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0] + coef[7, 1:7] = [-1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0] + coef[8, 1:7] = [1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0] + else + error("ENO 系数未实现 order=$spatial_order") + end +end + +# ---------------------- ENO 重构器 ---------------------- +mutable struct EnoReconstructor + spatial_order::Int + ntcells::Int + lmc::Vector{Int} + coef::Matrix{Float64} + dd::Matrix{Float64} + + function EnoReconstructor(spatial_order::Int, ntcells::Int) + lmc = zeros(Int, ntcells) + coef = zeros(Float64, spatial_order + 1, spatial_order) + dd = zeros(Float64, spatial_order, ntcells) + _init_eno_coef!(spatial_order, coef) + new(spatial_order, ntcells, lmc, coef, dd) + end +end + +function reconstruct(rec::EnoReconstructor, q::Vector{Float64}, cfd::Any) + # 1. 差商计算 (dd[1,:] = q) + @views rec.dd[1, :] .= q + for m in 2:rec.spatial_order + for j in 1:(rec.ntcells - m + 1) + rec.dd[m, j] = rec.dd[m-1, j+1] - rec.dd[m-1, j] + end + end + + # 2. 选择 smoothest stencil + domain = cfd.domain + for i in (domain.ist - 1):(domain.ied) # Python: range(ist-1, ied+1) → ied+1-1 = ied + rec.lmc[i] = i + for m in 2:rec.spatial_order + if abs(rec.dd[m, rec.lmc[i] - 1]) < abs(rec.dd[m, rec.lmc[i]]) + rec.lmc[i] -= 1 + end + end + end + + # 3. 重构界面值 + solution = cfd.solution + for i in domain.ist:(domain.ied) # Python: range(ist, ied+1) → ied+1-1 = ied + j = i - domain.ist + 1 # Julia 1-based + k1 = rec.lmc[i - 1] + k2 = rec.lmc[i] + r1 = (i - 1) - k1 + 1 + r2 = i - k2 + 1 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in 1:rec.spatial_order + solution.q_face_left[j] += q[k1 + m - 1] * rec.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m - 1] * rec.coef[r2, m] + end + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/julia/reconstructor/weno3.jl b/example/1d-linear-convection/weno3/julia/01i/julia/reconstructor/weno3.jl new file mode 100644 index 00000000..2b6fe1ab --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/julia/reconstructor/weno3.jl @@ -0,0 +1,65 @@ +# julia/reconstructor/weno3.jl +""" +WENO3 重构器(与 reconstructor/weno3.py 完全同构) +""" + +mutable struct Weno3Reconstructor + # 无字段,与 Python 一致 +end + +function reconstruct(rec::Weno3Reconstructor, q::Vector{Float64}, cfd::Any) + domain = cfd.domain + solution = cfd.solution + _reconstruct_left_interfaces(domain, q, solution.q_face_left) + _reconstruct_right_interfaces(domain, q, solution.q_face_right) +end + +function _reconstruct_left_interfaces(domain, u, qL) + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in (domain.ist - 1):(domain.ied - 1) + j = i - (domain.ist - 1) + 1 # ← Julia 1-based: j = i - (ist-1) 对应 Python j = i - (ist-1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = _reconstruct_from_left_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_right_interfaces(domain, u, qR) + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in domain.ist:domain.ied + j = i - domain.ist + 1 # ← Julia 1-based: j = i - ist 对应 Python j = i - ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = _reconstruct_from_right_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_from_left_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -0.5*v1 + 1.5*v2 # r=1 stencil + q1 = 0.5*v2 + 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end + +function _reconstruct_from_right_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 0.5*v1 + 0.5*v2 # r=1 stencil + q1 = 1.5*v2 - 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/julia/residual.jl b/example/1d-linear-convection/weno3/julia/01i/julia/residual.jl new file mode 100644 index 00000000..15cc9387 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/julia/residual.jl @@ -0,0 +1,65 @@ +# julia/residual.jl +""" +残差计算器(与 residual.py 完全同构) +- 封装重建→通量→散度完整流程 +- 依赖 cfd 的多个字段 +""" + +include("mesh.jl") + +mutable struct ResidualCalculator + cfd::Any + config::Any + domain::Any + solution::Any + mesh::Mesh + reconstructor::Any + flux_calculator::Any # 通量计算器(外部传入,替代工厂) + + function ResidualCalculator(cfd::Any, flux_calculator::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + mesh = domain.mesh + reconstructor = cfd.reconstructor + + new(cfd, config, domain, solution, mesh, reconstructor, flux_calculator) + end +end + +""" +计算完整残差(对外唯一接口) +""" +function compute!(calc::ResidualCalculator) + #_reconstruct(calc) + _compute_inviscid_flux(calc) + _compute_flux_divergence(calc) +end + +""" +私有方法:界面值重建 +""" +function _reconstruct(calc::ResidualCalculator) + reconstruct(calc.reconstructor, calc.solution.u, calc.cfd) +end + +""" +私有方法:计算无粘通量 +""" +function _compute_inviscid_flux(calc::ResidualCalculator) + compute!(calc.flux_calculator, + calc.solution.q_face_left, + calc.solution.q_face_right, + calc.solution.flux) +end + +""" +私有方法:计算通量散度(残差 = -dF/dx) +""" +function _compute_flux_divergence(calc::ResidualCalculator) + solution = calc.solution + mesh = calc.mesh + for i in 1:mesh.ncells + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / mesh.dx + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/julia/solution.jl b/example/1d-linear-convection/weno3/julia/01i/julia/solution.jl new file mode 100644 index 00000000..90ca0393 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/julia/solution.jl @@ -0,0 +1,75 @@ +# julia/solution.jl +""" +Solution 结构体(与真实 solution.py 完全同构) +字段顺序、方法、逻辑 1:1 对齐 +""" + +include("mesh.jl") +include("domain.jl") +include("initial_condition.jl") + +mutable struct Solution + domain::Domain + q_face_left::Vector{Float64} + q_face_right::Vector{Float64} + flux::Vector{Float64} + res::Vector{Float64} + u::Vector{Float64} + un::Vector{Float64} + + function Solution(config::Any, domain::Domain) + mesh = domain.mesh + + q_face_left = zeros(Float64, mesh.nnodes) + q_face_right = zeros(Float64, mesh.nnodes) + flux = zeros(Float64, mesh.nnodes) + res = zeros(Float64, mesh.ncells) + u = zeros(Float64, domain.ntcells) + un = zeros(Float64, domain.ntcells) + + sol = new(domain, q_face_left, q_face_right, flux, res, u, un) + initialize_from_config(sol, config) + return sol + end +end + +""" +重置解数组为初始状态 +""" +function reset_solution(sol::Solution) + fill!(sol.u, 0.0) + fill!(sol.un, 0.0) +end + +""" +根据配置初始化场 +注:暂用硬编码替代 InitialConditionFactory +""" +function initialize_from_config(sol::Solution, config::Any) + ic_type = getfield_safe(config, :ic_type, "step") + + # 硬编码创建 IC(替代 InitialConditionFactory) + ic = if ic_type == "step" + StepFunctionIC(config) + elseif ic_type == "sin" + SineWaveIC(config) + elseif ic_type == "gaussian" + GaussianPulseIC(config) + else + error("未知初始条件类型: $ic_type") + end + + apply(ic, sol) # 调用 IC.apply +end + +""" +更新旧场:un = u +""" +function update_old_field(sol::Solution) + sol.un .= sol.u # 等价于 Python 的 un[:] = u[:] +end + +# ---------------------- 辅助函数 ---------------------- +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/julia/test/test_boundary.jl b/example/1d-linear-convection/weno3/julia/01i/julia/test/test_boundary.jl new file mode 100644 index 00000000..ef27d682 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/julia/test/test_boundary.jl @@ -0,0 +1,57 @@ +# julia/test/test_boundary.jl +using NPZ +include("../boundary.jl") + +struct MockConfig + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + debug::Bool +end + +struct MockDomain + nghosts::Int + ist::Int # Julia 本地索引(1-based) + ied::Int + ntcells::Int +end + +struct MockCfd + config::MockConfig + domain::MockDomain +end + +# ===== 关键:使用 Julia 本地索引规则 ===== +nghosts = 2 +ncells = 40 +ist = nghosts + 1 # = 3 +ied = ist + ncells # = 43 +ntcells = ncells + 2 * nghosts # = 44 + +config = MockConfig("dirichlet", 0.5, 1.5, true) +domain = MockDomain(nghosts, ist, ied, ntcells) +cfd_mock = MockCfd(config, domain) + +# 加载 Python 生成的 u_input.npy +# 注意:Python u[0] → Julia u[1],所以内容完全对应 +u_input = npzread("../../python/u_input.npy") +@assert length(u_input) == ntcells "数组长度不匹配!" + +# 测试三种边界 +test_cases = [ + ("periodic", PeriodicBoundary(cfd_mock)), + ("dirichlet", DirichletBoundary(cfd_mock)), + ("neumann", NeumannBoundary(cfd_mock)) +] + +for (name, bc) in test_cases + u = copy(u_input) + apply!(bc, u) + + u_py = npzread("../../python/u_$(name)_py.npy") + err = maximum(abs.(u .- u_py)) + println("边界: $(name) | 最大误差: $(err)") + @assert err < 1e-12 "❌ $(name) 不一致!" +end + +println("✅ 所有边界条件测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/julia/test/test_domain.jl b/example/1d-linear-convection/weno3/julia/01i/julia/test/test_domain.jl new file mode 100644 index 00000000..7b423f77 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/julia/test/test_domain.jl @@ -0,0 +1,44 @@ +# julia/test/test_domain.jl +include("../mesh.jl") +include("../domain.jl") + +# MockConfig:模拟 Python CfdConfig +struct MockConfig + recon_scheme::String + spatial_order::Int +end + +# 测试 ENO +config_eno = MockConfig("eno", 2) +mesh = Mesh() +domain_eno = Domain(config_eno, mesh) + +println("ENO: nghosts = ", domain_eno.nghosts) # 2 +println("ENO: ist = ", domain_eno.ist) # 2 +println("ENO: ied = ", domain_eno.ied) # 42 +println("物理索引范围: ", collect(get_physical_indices(domain_eno))[1:3], " ... ", collect(get_physical_indices(domain_eno))[end-2:end]) + +# 测试 WENO(字符串 "weno") +config_weno = MockConfig("weno", 2) +domain_weno = Domain(config_weno, mesh) +println("WENO: nghosts = ", domain_weno.nghosts) # 2 + +# 测试 WENO3(字符串 "weno3") +config_weno3 = MockConfig("weno3", 2) +domain_weno3 = Domain(config_weno3, mesh) +println("WENO3: nghosts = ", domain_weno3.nghosts) # 2 + +# 测试 is_physical_cell +@assert is_physical_cell(domain_eno, 2) == true # ist=2 +@assert is_physical_cell(domain_eno, 41) == true # ied-1=41 +@assert is_physical_cell(domain_eno, 42) == false # ied=42 + +# ✅ 断言 +@assert domain_eno.nghosts == 2 +@assert domain_eno.ist == 2 +@assert domain_eno.ied == 42 +@assert domain_eno.ntcells == 44 +@assert domain_weno.nghosts == 2 +@assert domain_weno3.nghosts == 2 + +println("✅ Domain 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/julia/test/test_eno.jl b/example/1d-linear-convection/weno3/julia/01i/julia/test/test_eno.jl new file mode 100644 index 00000000..17b9a7a4 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/julia/test/test_eno.jl @@ -0,0 +1,34 @@ +# julia/test/test_eno.jl +include("../mesh.jl") +include("../domain.jl") +include("../solution.jl") +include("../reconstructor/eno.jl") + +struct MockCfd + domain::Domain + solution::Solution +end + +mesh = Mesh() +# 注意:Domain 使用 1-based 索引(ist = nghosts + 1) +domain = Domain((recon_scheme="eno", spatial_order=2), mesh) +solution = Solution((ic_type="step",), domain) + +# 线性初始场: u[i] = (i-1) * 0.1 +u = solution.u +for i in 1:domain.ntcells + u[i] = Float64(i-1) * 0.1 +end + +eno = EnoReconstructor(2, domain.ntcells) +cfd = MockCfd(domain, solution) +reconstruct(eno, u, cfd) + +println("q_face_left[1] = ", solution.q_face_left[1]) +println("q_face_right[1] = ", solution.q_face_right[1]) + +# ENO2 对线性函数应精确重构 0.15 +@assert abs(solution.q_face_left[1] - 0.15) < 1e-12 +@assert abs(solution.q_face_right[1] - 0.15) < 1e-12 + +println("✅ ENO 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/julia/test/test_flux.jl b/example/1d-linear-convection/weno3/julia/01i/julia/test/test_flux.jl new file mode 100644 index 00000000..0ebc1dc0 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/julia/test/test_flux.jl @@ -0,0 +1,53 @@ +# julia/test/test_flux.jl +include("../mesh.jl") +include("../flux.jl") + +# Mock 对象 +struct MockConfig + flux_type::String + wave_speed::Float64 +end + +struct MockDomain + mesh::Mesh +end + +struct MockCfd + config::MockConfig + domain::MockDomain +end + +# 创建 mock +mesh = Mesh() +config = MockConfig("rusanov", 1.0) +domain = MockDomain(mesh) +cfd = MockCfd(config, domain) + +# 测试 Rusanov +rusanov = RusanovFluxCalculator(cfd) +N = mesh.nnodes +qL = [1.0, 2.0, 3.0, 4.0, 5.0, zeros(Float64, N-5)...] +qR = [2.0, 3.0, 4.0, 5.0, 6.0, zeros(Float64, N-5)...] +flux_rus = zeros(Float64, N) +compute!(rusanov, qL, qR, flux_rus) + +println("Rusanov flux[1] = ", flux_rus[1]) # 应为 1.0 +@assert abs(flux_rus[1] - 1.0) < 1e-12 + +# 测试 Engquist-Osher +eo = EngquistOsherFluxCalculator(cfd) +flux_eo = zeros(Float64, N) +compute!(eo, qL, qR, flux_eo) + +println("EO flux[1] = ", flux_eo[1]) # c=1 → cp=1, cm=0 → flux=1*1 + 0*2 = 1.0 +@assert abs(flux_eo[1] - 1.0) < 1e-12 + +# 测试 c = -1.0 +config_neg = MockConfig("rusanov", -1.0) +cfd_neg = MockCfd(config_neg, domain) +rusanov_neg = RusanovFluxCalculator(cfd_neg) +compute!(rusanov_neg, qL, qR, flux_rus) +println("Rusanov (c=-1) flux[1] = ", flux_rus[1]) # F_L=-1, F_R=-2, Smax=1 → flux = -1.5 -0.5*(-1) = -1.0 +@assert abs(flux_rus[1] - (-2.0)) < 1e-12 # ← 修正:-2.0 而非 -1.0 + +println("✅ Flux 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/julia/test/test_initial_condition.jl b/example/1d-linear-convection/weno3/julia/01i/julia/test/test_initial_condition.jl new file mode 100644 index 00000000..dc58691a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/julia/test/test_initial_condition.jl @@ -0,0 +1,45 @@ +# julia/test/test_initial_condition.jl +using NPZ + +# 包含初始条件模块 +include("../initial_condition.jl") + +# 构造 mock config(与 Python 完全一致) +struct MockConfig + ic_type::String + domain_length::Float64 + pulse_center::Float64 + pulse_width::Float64 +end + +# 生成与 Python Mesh.xcc 完全相同的 x 坐标 +function generate_xcc() + xmin, xmax = 0.0, 2.0 + ncells = 40 + dx = (xmax - xmin) / ncells + xcc = Vector{Float64}(undef, ncells) + for i in 1:ncells + xcc[i] = xmin + (i - 0.5) * dx # i-1 + 0.5 → i-0.5 + end + return xcc +end + +# 主测试 +xcc = generate_xcc() + +test_cases = [ + ("step", StepFunctionIC(MockConfig("step", 2.0, 0.5, 0.1))), + ("sin", SineWaveIC(MockConfig("sin", 2.0, 0.5, 0.1))), + ("gaussian", GaussianPulseIC(MockConfig("gaussian", 2.0, 0.5, 0.1))) +] + +for (name, ic) in test_cases + u_jl = evaluate_at(ic, xcc) + u_py = npzread("../../python/u_$(name)_interior_py.npy") + + err = maximum(abs.(u_jl .- u_py)) + println("IC: $(name) | 最大误差: $(err)") + @assert err < 1e-12 "❌ $(name) 不一致!" +end + +println("✅ 所有初始条件测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/julia/test/test_mesh.jl b/example/1d-linear-convection/weno3/julia/01i/julia/test/test_mesh.jl new file mode 100644 index 00000000..315d2aac --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/julia/test/test_mesh.jl @@ -0,0 +1,37 @@ +# julia/test/test_mesh.jl +include("../mesh.jl") + +# 创建 mesh(与 Python 完全相同) +mesh = Mesh() + +# 打印关键值(与 Python 对比) +println("xmin = ", mesh.xmin) # 0.0 +println("xmax = ", mesh.xmax) # 2.0 +println("ncells = ", mesh.ncells) # 40 +println("nnodes = ", mesh.nnodes) # 41 +println("nx = ", mesh.nx) # 40 +println("L = ", mesh.L) # 2.0 +println("dx = ", mesh.dx) # 0.05 + +# 检查 x[1] (Python x[0]) 和 x[41] (Python x[40]) +println("x[1] = ", mesh.x[1]) # 0.0 +println("x[41] = ", mesh.x[41]) # 2.0 + +# 检查 xcc[1] (Python xcc[0]) 和 xcc[40] (Python xcc[39]) +println("xcc[1] = ", mesh.xcc[1]) # 0.025 +println("xcc[40] = ", mesh.xcc[40]) # 1.975 + +# ✅ 严格断言 +@assert mesh.xmin == 0.0 +@assert mesh.xmax == 2.0 +@assert mesh.ncells == 40 +@assert mesh.nnodes == 41 +@assert mesh.nx == 40 +@assert mesh.L == 2.0 +@assert mesh.dx == 0.05 +@assert mesh.x[1] == 0.0 +@assert mesh.x[41] == 2.0 +@assert abs(mesh.xcc[1] - 0.025) < 1e-12 +@assert abs(mesh.xcc[40] - 1.975) < 1e-12 + +println("✅ Mesh 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/julia/test/test_residual.jl b/example/1d-linear-convection/weno3/julia/01i/julia/test/test_residual.jl new file mode 100644 index 00000000..e37e8d2b --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/julia/test/test_residual.jl @@ -0,0 +1,53 @@ +# julia/test/test_residual.jl +include("../mesh.jl") +include("../domain.jl") +include("../solution.jl") +include("../flux.jl") +include("../residual.jl") + +# ===== Dummy Reconstructor ===== +struct DummyReconstructor end + +# ===== Mock Config ===== +struct MockConfig + flux_type::String + wave_speed::Float64 +end + +# ===== Mock CFD (必须包含 reconstructor 字段) ===== +struct MockCfd + config::MockConfig + domain::Domain + solution::Solution + reconstructor::DummyReconstructor # ← 关键:添加此字段 +end + +# ===== 主测试 ===== +config = MockConfig("rusanov", 1.0) +mesh = Mesh() +domain = Domain((recon_scheme="eno", spatial_order=2), mesh) +solution = Solution((ic_type="step",), domain) + +# 手动设置界面值(跳过重建) +for i in 1:mesh.nnodes + solution.q_face_left[i] = Float64(i) * 0.1 + solution.q_face_right[i] = Float64(i) * 0.1 +end + +flux_calc = RusanovFluxCalculator((config=config, domain=domain)) +dummy_recon = DummyReconstructor() +cfd = MockCfd(config, domain, solution, dummy_recon) + +# 创建残差计算器 +res_calc = ResidualCalculator(cfd, flux_calc) + +# 计算残差(注意:_reconstruct 仍被注释) +compute!(res_calc) + +println("flux[1] = ", solution.flux[1]) +println("res[1] = ", solution.res[1]) + +@assert abs(solution.flux[1] - 0.1) < 1e-12 +@assert abs(solution.res[1] - (-2.0)) < 1e-12 + +println("✅ Residual 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/julia/test/test_solution.jl b/example/1d-linear-convection/weno3/julia/01i/julia/test/test_solution.jl new file mode 100644 index 00000000..f44ff0f0 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/julia/test/test_solution.jl @@ -0,0 +1,42 @@ +# julia/test/test_solution.jl +include("../mesh.jl") +include("../domain.jl") +include("../solution.jl") + +# MockConfig +struct MockConfig + recon_scheme::String + spatial_order::Int + ic_type::String + domain_length::Float64 + pulse_center::Float64 + pulse_width::Float64 +end + +# 创建 solution +config = MockConfig("eno", 2, "step", 2.0, 0.5, 0.1) +mesh = Mesh() +domain = Domain(config, mesh) +sol = Solution(config, domain) + +# 检查字段尺寸 +@assert length(sol.q_face_left) == mesh.nnodes # 41 +@assert length(sol.flux) == mesh.nnodes # 41 +@assert length(sol.res) == mesh.ncells # 40 +@assert length(sol.u) == domain.ntcells # 44 + +# 检查初始场 +println("u[3] (物理起始): ", sol.u[3]) # 应为 1.0 +println("u[23] (x=1.0): ", sol.u[23]) # 应为 2.0 + +# 测试 update_old_field +sol.u[3] = 999.0 +update_old_field(sol) +@assert sol.un[3] == 999.0 + +# 测试 reset_solution +reset_solution(sol) +@assert sol.u[3] == 0.0 +@assert sol.un[3] == 0.0 + +println("✅ Solution 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/julia/test/test_time_integration.jl b/example/1d-linear-convection/weno3/julia/01i/julia/test/test_time_integration.jl new file mode 100644 index 00000000..091a8637 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/julia/test/test_time_integration.jl @@ -0,0 +1,53 @@ +# julia/test/test_time_integration.jl +include("../time_integration.jl") +include("../flux.jl") +include("../boundary.jl") + +# Mock CFD 对象 +struct MockCfd + config::Any + domain::Domain + solution::Solution + residual_calculator::Any + boundary_condition::Any +end + +# 创建完整链路 +mesh = Mesh() +domain = Domain((recon_scheme="eno", spatial_order=2), mesh) +solution = Solution((ic_type="step",), domain) + +# 手动设置界面值(跳过 reconstructor) +for i in 1:mesh.nnodes + solution.q_face_left[i] = Float64(i) * 0.1 + solution.q_face_right[i] = Float64(i) * 0.1 +end + +# Mock components +flux_calc = RusanovFluxCalculator((config=(wave_speed=1.0,), domain=domain)) +res_calc = ResidualCalculator((config=nothing, domain=domain, solution=solution, reconstructor=nothing), flux_calc) +bc = PeriodicBoundary((domain=domain, config=(debug=false,))) +cfd = MockCfd((rk_order=1,), domain, solution, res_calc, bc) + +# 测试 RK1 +rk1 = RK1Integrator(cfd) +step(rk1, 0.025) + +println("RK1 step completed") +@assert solution.u[3] != 0.0 # 应已更新 + +# 测试 RK2 +cfd2 = MockCfd((rk_order=2,), domain, solution, res_calc, bc) +rk2 = RK2Integrator(cfd2) +step(rk2, 0.025) + +println("RK2 step completed") + +# 测试 RK3 +cfd3 = MockCfd((rk_order=3,), domain, solution, res_calc, bc) +rk3 = RK3Integrator(cfd3) +step(rk3, 0.025) + +println("RK3 step completed") + +println("✅ Time Integration 逻辑测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/julia/test/test_weno3.jl b/example/1d-linear-convection/weno3/julia/01i/julia/test/test_weno3.jl new file mode 100644 index 00000000..9a91761e --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/julia/test/test_weno3.jl @@ -0,0 +1,47 @@ +# julia/test/test_weno3.jl +include("../mesh.jl") +include("../domain.jl") +include("../solution.jl") +include("../reconstructor/weno3.jl") + +# Mock CFD +struct MockCfd + domain::Domain + solution::Solution +end + +# 创建数据 +mesh = Mesh() +# 注意:Domain 现在使用 1-based 索引(ist = nghosts + 1) +domain = Domain((recon_scheme="weno3", spatial_order=2), mesh) +solution = Solution((ic_type="step",), domain) + +# 设置 u(物理区域 + ghost) +u = solution.u +for i in 1:domain.ntcells + u[i] = Float64(i-1) * 0.1 # u = [0.0, 0.1, 0.2, ..., 4.3] +end + +# 创建 reconstructor +weno3 = Weno3Reconstructor() +cfd = MockCfd(domain, solution) + +# 重建 +reconstruct(weno3, u, cfd) + +println("q_face_left length = ", length(solution.q_face_left)) # 应为 41 +println("q_face_right length = ", length(solution.q_face_right)) # 应为 41 +println("q_face_left[1] = ", solution.q_face_left[1]) +println("q_face_right[1] = ", solution.q_face_right[1]) + +# 验证左界面值(i = ist-1 = 2, j = 1) +# v1 = u[1] = 0.0, v2 = u[2] = 0.1, v3 = u[3] = 0.2 +# qL = w0*(-0.5*0.0 + 1.5*0.1) + w1*(0.5*0.1 + 0.5*0.2) = w0*0.15 + w1*0.15 = 0.15 +@assert abs(solution.q_face_left[1] - 0.15) < 1e-12 + +# 验证右界面值(i = ist = 3, j = 1) +# v1 = u[2] = 0.1, v2 = u[3] = 0.2, v3 = u[4] = 0.3 +# qR = w0*(0.5*0.1 + 0.5*0.2) + w1*(1.5*0.2 - 0.5*0.3) = w0*0.15 + w1*0.15 = 0.15 +@assert abs(solution.q_face_right[1] - 0.15) < 1e-12 + +println("✅ WENO3 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/julia/test_residual.jl b/example/1d-linear-convection/weno3/julia/01i/julia/test_residual.jl new file mode 100644 index 00000000..315d2aac --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/julia/test_residual.jl @@ -0,0 +1,37 @@ +# julia/test/test_mesh.jl +include("../mesh.jl") + +# 创建 mesh(与 Python 完全相同) +mesh = Mesh() + +# 打印关键值(与 Python 对比) +println("xmin = ", mesh.xmin) # 0.0 +println("xmax = ", mesh.xmax) # 2.0 +println("ncells = ", mesh.ncells) # 40 +println("nnodes = ", mesh.nnodes) # 41 +println("nx = ", mesh.nx) # 40 +println("L = ", mesh.L) # 2.0 +println("dx = ", mesh.dx) # 0.05 + +# 检查 x[1] (Python x[0]) 和 x[41] (Python x[40]) +println("x[1] = ", mesh.x[1]) # 0.0 +println("x[41] = ", mesh.x[41]) # 2.0 + +# 检查 xcc[1] (Python xcc[0]) 和 xcc[40] (Python xcc[39]) +println("xcc[1] = ", mesh.xcc[1]) # 0.025 +println("xcc[40] = ", mesh.xcc[40]) # 1.975 + +# ✅ 严格断言 +@assert mesh.xmin == 0.0 +@assert mesh.xmax == 2.0 +@assert mesh.ncells == 40 +@assert mesh.nnodes == 41 +@assert mesh.nx == 40 +@assert mesh.L == 2.0 +@assert mesh.dx == 0.05 +@assert mesh.x[1] == 0.0 +@assert mesh.x[41] == 2.0 +@assert abs(mesh.xcc[1] - 0.025) < 1e-12 +@assert abs(mesh.xcc[40] - 1.975) < 1e-12 + +println("✅ Mesh 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/julia/time_integration.jl b/example/1d-linear-convection/weno3/julia/01i/julia/time_integration.jl new file mode 100644 index 00000000..95e5cd67 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/julia/time_integration.jl @@ -0,0 +1,130 @@ +# julia/time_integration.jl +""" +时间推进器模块(与 time_integration.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("residual.jl") +include("boundary.jl") + +# ---------------------- 抽象时间推进器基类 ---------------------- +abstract type TimeIntegrator end + +mutable struct TimeIntegratorBase <: TimeIntegrator + cfd::Any + config::Any + domain::Domain + solution::Solution + residual_calculator::Any # ResidualCalculator +end + +function TimeIntegratorBase(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + residual_calculator = cfd.residual_calculator + TimeIntegratorBase(cfd, config, domain, solution, residual_calculator) +end + +function compute_residual(integrator::TimeIntegratorBase) + compute!(integrator.residual_calculator) +end + +function apply_boundary(integrator::TimeIntegratorBase) + apply!(integrator.cfd.boundary_condition, integrator.solution.u) +end + +function map_idx(integrator::TimeIntegratorBase, i::Int) + return i - integrator.domain.ist + 1 # ← +1 转为 1-based +end + +# ---------------------- RK1Integrator ---------------------- +mutable struct RK1Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK1Integrator(cfd::Any) + RK1Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK1Integrator, dt::Float64) + compute_residual(integrator.base) + for i in integrator.base.domain.ist:(integrator.base.domain.ied - 1) + j = map_idx(integrator.base, i) + integrator.base.solution.u[i] += dt * integrator.base.solution.res[j] + end + apply_boundary(integrator.base) + update_old_field(integrator.base.solution) +end + +# ---------------------- RK2Integrator ---------------------- +mutable struct RK2Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK2Integrator(cfd::Any) + RK2Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK2Integrator, dt::Float64) + base = integrator.base + # 阶段1:预测步 + compute_residual(base) + u_pred = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u_pred[i] += dt * base.solution.res[j] + end + base.solution.u .= u_pred + apply_boundary(base) + # 阶段2:校正步 + compute_residual(base) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = 0.5 * base.solution.un[i] + 0.5 * base.solution.u[i] + 0.5 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- RK3Integrator ---------------------- +mutable struct RK3Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK3Integrator(cfd::Any) + RK3Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK3Integrator, dt::Float64) + base = integrator.base + # 阶段1 + compute_residual(base) + u1 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u1[i] += dt * base.solution.res[j] + end + base.solution.u .= u1 + apply_boundary(base) + # 阶段2 + compute_residual(base) + u2 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u2[i] = 0.75 * base.solution.un[i] + 0.25 * base.solution.u[i] + 0.25 * dt * base.solution.res[j] + end + base.solution.u .= u2 + apply_boundary(base) + # 阶段3 + compute_residual(base) + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = c1 * base.solution.un[i] + c2 * base.solution.u[i] + c3 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/python/boundary.py b/example/1d-linear-convection/weno3/julia/01i/python/boundary.py new file mode 100644 index 00000000..6054f92d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/python/boundary.py @@ -0,0 +1,103 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/python/cfd_registry.py b/example/1d-linear-convection/weno3/julia/01i/python/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/python/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/python/config.py b/example/1d-linear-convection/weno3/julia/01i/python/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/python/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/python/domain.py b/example/1d-linear-convection/weno3/julia/01i/python/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/python/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/python/eno_weno_comparison.png b/example/1d-linear-convection/weno3/julia/01i/python/eno_weno_comparison.png new file mode 100644 index 00000000..8d7135ec Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01i/python/eno_weno_comparison.png differ diff --git a/example/1d-linear-convection/weno3/julia/01i/python/factories/base_factory.py b/example/1d-linear-convection/weno3/julia/01i/python/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/python/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/python/flux.py b/example/1d-linear-convection/weno3/julia/01i/python/flux.py new file mode 100644 index 00000000..5ac73aa8 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/python/flux.py @@ -0,0 +1,74 @@ +# flux.py +""" +通量计算器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册,替代硬编码工厂 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象通量计算基类(统一接口) ---------------------- +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass + +# ---------------------- 2. 具体通量计算子类(使用装饰器注册) ---------------------- + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + +class FluxCalculatorFactory: + """通量计算器工厂""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD对象 + + Returns: + 通量计算器实例 + """ + from factories.base_factory import BaseFactory + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/python/gen_boundary_test_data.py b/example/1d-linear-convection/weno3/julia/01i/python/gen_boundary_test_data.py new file mode 100644 index 00000000..c7fc2a9c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/python/gen_boundary_test_data.py @@ -0,0 +1,35 @@ +# python/gen_boundary_test_data.py +import numpy as np +from config import CfdConfig +from mesh import Mesh +from domain import Domain + +# 固定测试配置 +config = CfdConfig() +config.with_boundary("dirichlet", left_value=0.5, right_value=1.5) +config.debug = True + +mesh = Mesh() +domain = Domain(config, mesh) + +# 构造 mock CFD 对象(仅含 config + domain) +class MockCfd: + def __init__(self, config, domain): + self.config = config + self.domain = domain + +# 测试用 u:0,1,2,...,N-1 +u_input = np.arange(domain.ntcells, dtype=np.float64) +np.save("u_input.npy", u_input) + +# 测试每种边界 +from boundary import PeriodicBoundary, DirichletBoundary, NeumannBoundary + +for bc_name, bc_class in [("periodic", PeriodicBoundary), ("dirichlet", DirichletBoundary), ("neumann", NeumannBoundary)]: + u = u_input.copy() + cfd_mock = MockCfd(config, domain) + bc = bc_class(cfd_mock) + bc.apply(u) + np.save(f"u_{bc_name}_py.npy", u) + +print("✅ 测试数据已生成:u_*.npy") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/python/gen_ic_test_data.py b/example/1d-linear-convection/weno3/julia/01i/python/gen_ic_test_data.py new file mode 100644 index 00000000..69c2573b --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/python/gen_ic_test_data.py @@ -0,0 +1,27 @@ +# python/gen_ic_test_data.py +import sys, os +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +import numpy as np +from config import CfdConfig +from mesh import Mesh +from domain import Domain +from solution import Solution + +# 固定 mesh +mesh = Mesh() +config = CfdConfig() + +# 测试三种 IC +for ic_type in ["step", "sin", "gaussian"]: + config.ic_type = ic_type + domain = Domain(config, mesh) + sol = Solution(config, domain) + + u_full = sol.u.copy() # 包含 ghost + u_interior = sol.u[domain.ist:domain.ied].copy() + + np.save(f"u_{ic_type}_full_py.npy", u_full) + np.save(f"u_{ic_type}_interior_py.npy", u_interior) + +print("✅ 初始条件测试数据已生成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/python/initial_condition.py b/example/1d-linear-convection/weno3/julia/01i/python/initial_condition.py new file mode 100644 index 00000000..047415b7 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/python/initial_condition.py @@ -0,0 +1,90 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, ic_type: str, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', ic_type, config) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/python/mesh.py b/example/1d-linear-convection/weno3/julia/01i/python/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/python/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/python/plotter.py b/example/1d-linear-convection/weno3/julia/01i/python/plotter.py new file mode 100644 index 00000000..9f1a414f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/python/plotter.py @@ -0,0 +1,107 @@ +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/python/reconstructor/__init__.py b/example/1d-linear-convection/weno3/julia/01i/python/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/python/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/python/reconstructor/base.py b/example/1d-linear-convection/weno3/julia/01i/python/reconstructor/base.py new file mode 100644 index 00000000..bbd63850 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/python/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def reconstruct(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/python/reconstructor/eno.py b/example/1d-linear-convection/weno3/julia/01i/python/reconstructor/eno.py new file mode 100644 index 00000000..c2fb385d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/python/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def reconstruct(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/python/reconstructor/factory.py b/example/1d-linear-convection/weno3/julia/01i/python/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/python/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/python/reconstructor/weno3.py b/example/1d-linear-convection/weno3/julia/01i/python/reconstructor/weno3.py new file mode 100644 index 00000000..bf68be50 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/python/reconstructor/weno3.py @@ -0,0 +1,59 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def reconstruct(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/julia/01i/python/registry.py b/example/1d-linear-convection/weno3/julia/01i/python/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/python/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/python/residual.py b/example/1d-linear-convection/weno3/julia/01i/python/residual.py new file mode 100644 index 00000000..b4d4d7dc --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/python/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._reconstruct() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _reconstruct(self): + """私有方法:界面值重建""" + self.reconstructor.reconstruct(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/python/run_eno_weno.py b/example/1d-linear-convection/weno3/julia/01i/python/run_eno_weno.py new file mode 100644 index 00000000..fd7f3874 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/python/run_eno_weno.py @@ -0,0 +1,52 @@ +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/python/solution.py b/example/1d-linear-convection/weno3/julia/01i/python/solution.py new file mode 100644 index 00000000..92a46d3f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/python/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config.ic_type, config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/python/solver.py b/example/1d-linear-convection/weno3/julia/01i/python/solver.py new file mode 100644 index 00000000..0d0b442d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/python/solver.py @@ -0,0 +1,86 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + + +from flux import InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory + +from boundary import BoundaryCondition, PeriodicBoundary, DirichletBoundary, NeumannBoundary, BoundaryConditionFactory + +from time_integration import TimeIntegrator, RK1Integrator, RK2Integrator, RK3Integrator, TimeIntegratorFactory + +from mesh import Mesh + +from reconstructor import ReconstructorFactory + +from initial_condition import InitialCondition, StepFunctionIC, SineWaveIC, GaussianPulseIC, InitialConditionFactory + +from domain import Domain +from solution import Solution + +from config import CfdConfig +from residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config.ic_type, self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + #runge_kutta(self) + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/julia/01i/python/time_integration.py b/example/1d-linear-convection/weno3/julia/01i/python/time_integration.py new file mode 100644 index 00000000..25ac0b4c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01i/python/time_integration.py @@ -0,0 +1,125 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class TimeIntegratorFactory: + """时间推进器工厂""" + + @staticmethod + def create(cfd) -> 'TimeIntegrator': + """ + 创建时间推进器实例 + + Args: + cfd: CFD对象 + + Returns: + 时间推进器实例 + """ + from factories.base_factory import BaseFactory + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01i/python/u_dirichlet_py.npy b/example/1d-linear-convection/weno3/julia/01i/python/u_dirichlet_py.npy new file mode 100644 index 00000000..3aa20bd2 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01i/python/u_dirichlet_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01i/python/u_gaussian_full_py.npy b/example/1d-linear-convection/weno3/julia/01i/python/u_gaussian_full_py.npy new file mode 100644 index 00000000..b762f0fe Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01i/python/u_gaussian_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01i/python/u_gaussian_interior_py.npy b/example/1d-linear-convection/weno3/julia/01i/python/u_gaussian_interior_py.npy new file mode 100644 index 00000000..68af8954 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01i/python/u_gaussian_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01i/python/u_input.npy b/example/1d-linear-convection/weno3/julia/01i/python/u_input.npy new file mode 100644 index 00000000..ef506fa7 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01i/python/u_input.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01i/python/u_neumann_py.npy b/example/1d-linear-convection/weno3/julia/01i/python/u_neumann_py.npy new file mode 100644 index 00000000..fa723d1e Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01i/python/u_neumann_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01i/python/u_periodic_py.npy b/example/1d-linear-convection/weno3/julia/01i/python/u_periodic_py.npy new file mode 100644 index 00000000..156aff85 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01i/python/u_periodic_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01i/python/u_sin_full_py.npy b/example/1d-linear-convection/weno3/julia/01i/python/u_sin_full_py.npy new file mode 100644 index 00000000..b87f8a13 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01i/python/u_sin_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01i/python/u_sin_interior_py.npy b/example/1d-linear-convection/weno3/julia/01i/python/u_sin_interior_py.npy new file mode 100644 index 00000000..6803fbfb Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01i/python/u_sin_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01i/python/u_step_full_py.npy b/example/1d-linear-convection/weno3/julia/01i/python/u_step_full_py.npy new file mode 100644 index 00000000..2fc0e183 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01i/python/u_step_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01i/python/u_step_interior_py.npy b/example/1d-linear-convection/weno3/julia/01i/python/u_step_interior_py.npy new file mode 100644 index 00000000..b5ad6600 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01i/python/u_step_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/boundary.jl b/example/1d-linear-convection/weno3/julia/01j/julia/boundary.jl new file mode 100644 index 00000000..5b0baaf4 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/boundary.jl @@ -0,0 +1,90 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/config.jl b/example/1d-linear-convection/weno3/julia/01j/julia/config.jl new file mode 100644 index 00000000..bf48de3a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/config.jl @@ -0,0 +1,68 @@ +# julia/config.jl +""" +CfdConfig:与 Python config.py 完全同构 +""" +mutable struct CfdConfig + ic_type::String + recon_scheme::String + flux_type::String + rk_order::Int + wave_speed::Float64 + final_time::Float64 + dt::Float64 + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + spatial_order::Int + + function CfdConfig() + new( + "step", + "eno", + "rusanov", + 1, + 1.0, + 0.625, + 0.025, + "periodic", + 1.0, + 2.0, + 2 + ) + end +end + +""" +专用配置:重建方案(链式调用) +""" +function with_reconstruction(cfg::CfdConfig, scheme::String, order::Union{Int, Nothing}=nothing) + cfg.recon_scheme = lowercase(scheme) + + if order !== nothing + cfg.spatial_order = order + else + if startswith(cfg.recon_scheme, "weno") + cfg.spatial_order = 5 + elseif cfg.recon_scheme == "eno" + cfg.spatial_order = 3 + else + error("不支持的重建格式:$scheme(仅支持 eno/weno)") + end + end + + return cfg # 支持链式调用 +end + +""" +专用配置:边界条件(链式调用) +""" +function with_boundary(cfg::CfdConfig, bc_type::String; left_value=nothing, right_value=nothing) + cfg.boundary_type = bc_type + if left_value !== nothing + cfg.left_boundary_value = left_value + end + if right_value !== nothing + cfg.right_boundary_value = right_value + end + return cfg +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/domain.jl b/example/1d-linear-convection/weno3/julia/01j/julia/domain.jl new file mode 100644 index 00000000..a7edc226 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/domain.jl @@ -0,0 +1,61 @@ +# julia/domain.jl +""" +Domain 结构体(与 domain.py 完全同构) +- 保存 config +- nghosts 计算逻辑 1:1 +- ist/ied 为 0-based(与 Python 一致) +""" + +include("mesh.jl") + +mutable struct Domain + config::Any # 保存 config(与 Python 一致) + mesh::Mesh + nghosts::Int + ist::Int # 0-based + ied::Int # 0-based + ntcells::Int +end + +function Domain(config::Any, mesh::Mesh) + nghosts = _calc_nghosts(config) + ist = nghosts + 1 # ← 1-based 起始索引 + ied = ist + mesh.ncells # ← 1-based 结束索引(不包含) + ntcells = mesh.ncells + 2 * nghosts + Domain(config, mesh, nghosts, ist, ied, ntcells) +end + +function _calc_nghosts(config::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + order = getfield_safe(config, :spatial_order, 2) + + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + if scheme == "eno" + nghosts = order + elseif startswith(scheme, "weno") + nghosts = order ÷ 2 + 1 + else + error("未知重建格式 $(scheme),无法计算ghost层!") + end + + if nghosts <= 0 + error("计算得到的ghost层数量无效:$(nghosts)(阶数$(order),格式$(scheme))") + end + + return nghosts +end + +function is_physical_cell(domain::Domain, idx::Int) + return domain.ist <= idx < domain.ied +end + +function get_physical_indices(domain::Domain) + return domain.ist:(domain.ied - 1) +end + +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/flux.jl b/example/1d-linear-convection/weno3/julia/01j/julia/flux.jl new file mode 100644 index 00000000..047598f7 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/flux.jl @@ -0,0 +1,70 @@ +# julia/flux.jl +""" +通量计算器模块(与 flux.py 完全同构) +- 抽象基类 + 具体实现 +- 字段:cfd, config, mesh, wave_speed +""" + +include("mesh.jl") + +# ---------------------- 抽象基类 ---------------------- +""" +InviscidFluxCalculator 抽象类型 +Julia 无 ABC,用文档约定 +所有子类型必须实现 compute! +""" +abstract type InviscidFluxCalculator end + +# ---------------------- RusanovFluxCalculator ---------------------- +mutable struct RusanovFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function RusanovFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::RusanovFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = calc.wave_speed + c_R = calc.wave_speed + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + end +end + +# ---------------------- EngquistOsherFluxCalculator ---------------------- +mutable struct EngquistOsherFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function EngquistOsherFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::EngquistOsherFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + c = calc.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/initial_condition.jl b/example/1d-linear-convection/weno3/julia/01j/julia/initial_condition.jl new file mode 100644 index 00000000..5e029e8d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/initial_condition.jl @@ -0,0 +1,86 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/mesh.jl b/example/1d-linear-convection/weno3/julia/01j/julia/mesh.jl new file mode 100644 index 00000000..1b0dd4bf --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/mesh.jl @@ -0,0 +1,45 @@ +# julia/mesh.jl +""" +Mesh 结构体(与提供的 mesh.py 完全同构) +- 字段名、初始化顺序、循环逻辑完全一致 +- 不做任何泛化或优化 +""" + +mutable struct Mesh + xmin::Float64 + xmax::Float64 + ncells::Int + nnodes::Int + nx::Int + x::Vector{Float64} + xcc::Vector{Float64} + L::Float64 # 在 init_mesh 中赋值 + dx::Float64 # 在 init_mesh 中赋值 + + function Mesh() + # 与 Python __init__ 完全一致 + xmin = 0.0 + xmax = 2.0 + ncells = 40 + nnodes = ncells + 1 + nx = ncells + x = zeros(Float64, nnodes) + xcc = zeros(Float64, ncells) + + # 调用 init_mesh(模拟 Python) + L = xmax - xmin + dx = L / ncells + + # Generate node coordinates: for i in range(self.nnodes) + for i in 1:nnodes + x[i] = xmin + (i - 1) * dx # Python i → Julia i-1 + end + + # Generate cell center coordinates + for i in 1:ncells + xcc[i] = 0.5 * (x[i] + x[i+1]) + end + + new(xmin, xmax, ncells, nnodes, nx, x, xcc, L, dx) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/plotter.jl b/example/1d-linear-convection/weno3/julia/01j/julia/plotter.jl new file mode 100644 index 00000000..a77d8d68 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/plotter.jl @@ -0,0 +1,147 @@ +# julia/plotter.jl +""" +CFDPlotter 的 Julia 实现(通过 PythonCall.jl 调用 Matplotlib) +确保与 Python plotter.py 行为完全一致 +""" + +using PythonCall + +# 初始化 Python 环境(加载 matplotlib, inflect) +const plt = pyimport("matplotlib.pyplot") +const inflect = pyimport("inflect") + +mutable struct CFDPlotter + default_styles::Dict{String, Any} + p::Py +end + +function CFDPlotter() + default_styles = Dict{String, Any}( + "numerical" => Dict( + :color => "blue", + :linestyle => "-", + :marker => "o", + :markerfacecolor => "none" + ), + "analytical" => Dict( + :color => "red", + :linestyle => "--", + :marker => "", + :linewidth => 1.5 + ), + "comparison" => [ + Dict(:color => "black", :linestyle => "-", :marker => "o", :markerfacecolor => "none"), + Dict(:color => "blue", :linestyle => "--", :marker => "s", :markerfacecolor => "none"), + Dict(:color => "green", :linestyle => ":", :marker => "^", :markerfacecolor => "none") + ] + ) + p = inflect.engine() + CFDPlotter(default_styles, p) +end + +""" +轻量即时绘图(快速验证结果) +""" +function plot_quick(plotter::CFDPlotter, cfd_result::Dict; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + rk_order = cfd_result["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = cfd_result["config"]["final_time"] + order = cfd_result["config"]["order"] + scheme = uppercase(cfd_result["config"]["scheme"]) + actual_title = "1D Convection (t=$(final_time))\n$(order)th-order $(scheme) + $(rk_str)-order RK" + end + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"]; + label="Numerical ($(uppercase(cfd_result["config"]["scheme"])))", + plotter.default_styles["numerical"]..., + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + # 通用样式 + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +""" +多格式/多精度对比绘图 +""" +function plot_comparison(plotter::CFDPlotter, result_list::Vector{Dict{String, Any}}; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + schemes = [uppercase(r["config"]["scheme"]) * string(r["config"]["order"]) for r in result_list] + rk_order = result_list[1]["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = result_list[1]["config"]["final_time"] + actual_title = "1D Convection Comparison (t=$(final_time))\n$(join(schemes, ", ")) + $(rk_str)-order RK" + end + + # 绘制多个数值解 + for (i, res) in enumerate(result_list) + style = plotter.default_styles["comparison"][mod1(i, length(plotter.default_styles["comparison"]))] + label = "Numerical ($(uppercase(res["config"]["scheme"]))$(res["config"]["order"]))" + plt.plot( + res["x"], res["numerical"]; + label=label, + style..., + markersize=5, linewidth=0.5 + ) + end + + # 绘制解析解 + plt.plot( + result_list[1]["x"], result_list[1]["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +function _set_common_style(plotter::CFDPlotter, title::String) + plt.title(title, fontsize=12) + plt.xlabel("x", fontsize=10) + plt.ylabel("u", fontsize=10) + plt.legend(fontsize=9) + plt.grid(true, color="gray", linestyle="--", linewidth=0.5, alpha=0.7) + plt.tight_layout() +end + +""" +快捷函数:ENO/WENO对比绘图 +""" +function plot_eno_weno_comparison(eno_result::Dict, weno_result::Dict; save_path=nothing) + plotter = CFDPlotter() + plot_comparison(plotter, [eno_result, weno_result]; save_path=save_path) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/reconstructor/eno.jl b/example/1d-linear-convection/weno3/julia/01j/julia/reconstructor/eno.jl new file mode 100644 index 00000000..e78a636f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/reconstructor/eno.jl @@ -0,0 +1,107 @@ +# julia/reconstructor/eno.jl +""" +ENO 重构器(与 reconstructor/eno.py 完全同构) +""" + +# ---------------------- ENO 系数初始化 ---------------------- +function _init_eno_coef!(spatial_order::Int, coef::Matrix{Float64}) + if spatial_order == 1 + coef[1, 1] = 1.0 + coef[2, 1] = 1.0 + elseif spatial_order == 2 + coef[1, 1:2] = [3.0/2.0, -1.0/2.0] + coef[2, 1:2] = [1.0/2.0, 1.0/2.0] + coef[3, 1:2] = [-1.0/2.0, 3.0/2.0] + elseif spatial_order == 3 + coef[1, 1:3] = [11.0/6.0, -7.0/6.0, 1.0/3.0] + coef[2, 1:3] = [1.0/3.0, 5.0/6.0, -1.0/6.0] + coef[3, 1:3] = [-1.0/6.0, 5.0/6.0, 1.0/3.0] + coef[4, 1:3] = [1.0/3.0, -7.0/6.0, 11.0/6.0] + elseif spatial_order == 4 + coef[1, 1:4] = [25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0] + coef[2, 1:4] = [1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0] + coef[3, 1:4] = [-1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0] + coef[4, 1:4] = [1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0] + coef[5, 1:4] = [-1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0] + elseif spatial_order == 5 + coef[1, 1:5] = [137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0] + coef[2, 1:5] = [1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0] + coef[3, 1:5] = [-1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0] + coef[4, 1:5] = [1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0] + coef[5, 1:5] = [-1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0] + coef[6, 1:5] = [1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0] + elseif spatial_order == 6 + coef[1, 1:6] = [49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0] + coef[2, 1:6] = [1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0] + coef[3, 1:6] = [-1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0] + coef[4, 1:6] = [1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0] + coef[5, 1:6] = [-1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0] + coef[6, 1:6] = [1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0] + coef[7, 1:6] = [-1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0] + elseif spatial_order == 7 + coef[1, 1:7] = [363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0] + coef[2, 1:7] = [1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0] + coef[3, 1:7] = [-1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0] + coef[4, 1:7] = [1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0] + coef[5, 1:7] = [-1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0] + coef[6, 1:7] = [1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0] + coef[7, 1:7] = [-1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0] + coef[8, 1:7] = [1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0] + else + error("ENO 系数未实现 order=$spatial_order") + end +end + +# ---------------------- ENO 重构器 ---------------------- +mutable struct EnoReconstructor + spatial_order::Int + ntcells::Int + lmc::Vector{Int} + coef::Matrix{Float64} + dd::Matrix{Float64} + + function EnoReconstructor(spatial_order::Int, ntcells::Int) + lmc = zeros(Int, ntcells) + coef = zeros(Float64, spatial_order + 1, spatial_order) + dd = zeros(Float64, spatial_order, ntcells) + _init_eno_coef!(spatial_order, coef) + new(spatial_order, ntcells, lmc, coef, dd) + end +end + +function reconstruct(rec::EnoReconstructor, q::Vector{Float64}, cfd::Any) + # 1. 差商计算 (dd[1,:] = q) + @views rec.dd[1, :] .= q + for m in 2:rec.spatial_order + for j in 1:(rec.ntcells - m + 1) + rec.dd[m, j] = rec.dd[m-1, j+1] - rec.dd[m-1, j] + end + end + + # 2. 选择 smoothest stencil + domain = cfd.domain + for i in (domain.ist - 1):(domain.ied) # Python: range(ist-1, ied+1) → ied+1-1 = ied + rec.lmc[i] = i + for m in 2:rec.spatial_order + if abs(rec.dd[m, rec.lmc[i] - 1]) < abs(rec.dd[m, rec.lmc[i]]) + rec.lmc[i] -= 1 + end + end + end + + # 3. 重构界面值 + solution = cfd.solution + for i in domain.ist:(domain.ied) # Python: range(ist, ied+1) → ied+1-1 = ied + j = i - domain.ist + 1 # Julia 1-based + k1 = rec.lmc[i - 1] + k2 = rec.lmc[i] + r1 = (i - 1) - k1 + 1 + r2 = i - k2 + 1 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in 1:rec.spatial_order + solution.q_face_left[j] += q[k1 + m - 1] * rec.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m - 1] * rec.coef[r2, m] + end + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/reconstructor/weno3.jl b/example/1d-linear-convection/weno3/julia/01j/julia/reconstructor/weno3.jl new file mode 100644 index 00000000..2b6fe1ab --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/reconstructor/weno3.jl @@ -0,0 +1,65 @@ +# julia/reconstructor/weno3.jl +""" +WENO3 重构器(与 reconstructor/weno3.py 完全同构) +""" + +mutable struct Weno3Reconstructor + # 无字段,与 Python 一致 +end + +function reconstruct(rec::Weno3Reconstructor, q::Vector{Float64}, cfd::Any) + domain = cfd.domain + solution = cfd.solution + _reconstruct_left_interfaces(domain, q, solution.q_face_left) + _reconstruct_right_interfaces(domain, q, solution.q_face_right) +end + +function _reconstruct_left_interfaces(domain, u, qL) + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in (domain.ist - 1):(domain.ied - 1) + j = i - (domain.ist - 1) + 1 # ← Julia 1-based: j = i - (ist-1) 对应 Python j = i - (ist-1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = _reconstruct_from_left_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_right_interfaces(domain, u, qR) + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in domain.ist:domain.ied + j = i - domain.ist + 1 # ← Julia 1-based: j = i - ist 对应 Python j = i - ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = _reconstruct_from_right_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_from_left_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -0.5*v1 + 1.5*v2 # r=1 stencil + q1 = 0.5*v2 + 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end + +function _reconstruct_from_right_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 0.5*v1 + 0.5*v2 # r=1 stencil + q1 = 1.5*v2 - 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/residual.jl b/example/1d-linear-convection/weno3/julia/01j/julia/residual.jl new file mode 100644 index 00000000..e8fd6584 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/residual.jl @@ -0,0 +1,65 @@ +# julia/residual.jl +""" +残差计算器(与 residual.py 完全同构) +- 封装重建→通量→散度完整流程 +- 依赖 cfd 的多个字段 +""" + +include("mesh.jl") + +mutable struct ResidualCalculator + cfd::Any + config::Any + domain::Any + solution::Any + mesh::Mesh + reconstructor::Any + flux_calculator::Any # 通量计算器(外部传入,替代工厂) + + function ResidualCalculator(cfd::Any, flux_calculator::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + mesh = domain.mesh + reconstructor = cfd.reconstructor + + new(cfd, config, domain, solution, mesh, reconstructor, flux_calculator) + end +end + +""" +计算完整残差(对外唯一接口) +""" +function compute!(calc::ResidualCalculator) + _reconstruct(calc) + _compute_inviscid_flux(calc) + _compute_flux_divergence(calc) +end + +""" +私有方法:界面值重建 +""" +function _reconstruct(calc::ResidualCalculator) + reconstruct(calc.reconstructor, calc.solution.u, calc.cfd) +end + +""" +私有方法:计算无粘通量 +""" +function _compute_inviscid_flux(calc::ResidualCalculator) + compute!(calc.flux_calculator, + calc.solution.q_face_left, + calc.solution.q_face_right, + calc.solution.flux) +end + +""" +私有方法:计算通量散度(残差 = -dF/dx) +""" +function _compute_flux_divergence(calc::ResidualCalculator) + solution = calc.solution + mesh = calc.mesh + for i in 1:mesh.ncells + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / mesh.dx + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/run_eno_weno.jl b/example/1d-linear-convection/weno3/julia/01j/julia/run_eno_weno.jl new file mode 100644 index 00000000..58b6cfa5 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/run_eno_weno.jl @@ -0,0 +1,50 @@ +# julia/run_eno_weno.jl +""" +1:1 复刻 run_eno_weno.py 的 Julia 版本 +""" + +include("config.jl") +include("mesh.jl") +include("solver.jl") +include("plotter.jl") + +function performEnoWenoAnalysis() + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + println("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + with_reconstruction(config_eno3, "eno", 3) # 显式指定 3 阶 + config_eno3.dt = 0.0025 # 覆盖默认值 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + run!(cfd_eno3) # 求解并生成 result 字典 + + # 3. 配置并运行 WENO3 求解 + println("Running WENO3 solver...") + config_weno3 = CfdConfig() + with_reconstruction(config_weno3, "weno", 3) # 显式指定 3 阶(WENO 默认 5 阶) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + run!(cfd_weno3) + + # 5. 绘制 ENO/WENO 对比图 + println("Plotting comparison results...") + plot_eno_weno_comparison( + cfd_eno3.result, + cfd_weno3.result; + save_path="eno_weno_comparison.png" + ) + + return cfd_eno3, cfd_weno3 +end + +# 主程序入口 +if abspath(PROGRAM_FILE) == @__FILE__ + performEnoWenoAnalysis() + println("Analysis completed!") +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/solution.jl b/example/1d-linear-convection/weno3/julia/01j/julia/solution.jl new file mode 100644 index 00000000..90ca0393 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/solution.jl @@ -0,0 +1,75 @@ +# julia/solution.jl +""" +Solution 结构体(与真实 solution.py 完全同构) +字段顺序、方法、逻辑 1:1 对齐 +""" + +include("mesh.jl") +include("domain.jl") +include("initial_condition.jl") + +mutable struct Solution + domain::Domain + q_face_left::Vector{Float64} + q_face_right::Vector{Float64} + flux::Vector{Float64} + res::Vector{Float64} + u::Vector{Float64} + un::Vector{Float64} + + function Solution(config::Any, domain::Domain) + mesh = domain.mesh + + q_face_left = zeros(Float64, mesh.nnodes) + q_face_right = zeros(Float64, mesh.nnodes) + flux = zeros(Float64, mesh.nnodes) + res = zeros(Float64, mesh.ncells) + u = zeros(Float64, domain.ntcells) + un = zeros(Float64, domain.ntcells) + + sol = new(domain, q_face_left, q_face_right, flux, res, u, un) + initialize_from_config(sol, config) + return sol + end +end + +""" +重置解数组为初始状态 +""" +function reset_solution(sol::Solution) + fill!(sol.u, 0.0) + fill!(sol.un, 0.0) +end + +""" +根据配置初始化场 +注:暂用硬编码替代 InitialConditionFactory +""" +function initialize_from_config(sol::Solution, config::Any) + ic_type = getfield_safe(config, :ic_type, "step") + + # 硬编码创建 IC(替代 InitialConditionFactory) + ic = if ic_type == "step" + StepFunctionIC(config) + elseif ic_type == "sin" + SineWaveIC(config) + elseif ic_type == "gaussian" + GaussianPulseIC(config) + else + error("未知初始条件类型: $ic_type") + end + + apply(ic, sol) # 调用 IC.apply +end + +""" +更新旧场:un = u +""" +function update_old_field(sol::Solution) + sol.un .= sol.u # 等价于 Python 的 un[:] = u[:] +end + +# ---------------------- 辅助函数 ---------------------- +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/solver.jl b/example/1d-linear-convection/weno3/julia/01j/julia/solver.jl new file mode 100644 index 00000000..05662894 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/solver.jl @@ -0,0 +1,167 @@ +# julia/solver.jl +""" +CFD 求解器主类(与 solver.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("initial_condition.jl") +include("boundary.jl") +include("flux.jl") +include("residual.jl") +include("time_integration.jl") +include("reconstructor/eno.jl") +include("reconstructor/weno3.jl") + +# ---------------------- Cfd 求解器 ---------------------- +mutable struct Cfd + config::Any + domain::Domain + solution::Solution + reconstructor::Any + residual_calculator::ResidualCalculator + integrator::Any + boundary_condition::Any + result::Dict{String, Any} + + function Cfd(config::Any, mesh::Mesh) + domain = Domain(config, mesh) + solution = Solution(config, domain) + + # 在 Cfd 构造函数中 + # =============== 重建器创建 =============== + recon_scheme = config.recon_scheme + spatial_order = config.spatial_order + + # ✅ 模拟 Python ReconstructorFactory 的逻辑 + if recon_scheme == "weno" + recon_scheme = "weno$(spatial_order)" + end + + # 现在根据 recon_scheme 创建 + reconstructor = if recon_scheme == "eno" + EnoReconstructor(spatial_order, domain.ntcells) + elseif recon_scheme == "weno3" + Weno3Reconstructor() + else + error("不支持的重建格式: $recon_scheme") + end + + # 通量计算器 + flux_calculator = if config.flux_type == "rusanov" + RusanovFluxCalculator((config=config, domain=domain)) + elseif config.flux_type == "engquist-osher" + EngquistOsherFluxCalculator((config=config, domain=domain)) + else + error("不支持的通量类型: $(config.flux_type)") + end + + # 残差计算器 + residual_calculator = ResidualCalculator( + (config=config, domain=domain, solution=solution, reconstructor=reconstructor), + flux_calculator + ) + + # 边界条件 + boundary_condition = if config.boundary_type == "periodic" + PeriodicBoundary((config=config, domain=domain)) + elseif config.boundary_type == "dirichlet" + DirichletBoundary((config=config, domain=domain)) + elseif config.boundary_type == "neumann" + NeumannBoundary((config=config, domain=domain)) + else + error("不支持的边界类型: $(config.boundary_type)") + end + + # 时间推进器 + integrator = if config.rk_order == 1 + RK1Integrator((config=config, domain=domain, solution=solution, residual_calculator=residual_calculator, boundary_condition=boundary_condition)) + elseif config.rk_order == 2 + RK2Integrator((config=config, domain=domain, solution=solution, residual_calculator=residual_calculator, boundary_condition=boundary_condition)) + elseif config.rk_order == 3 + RK3Integrator((config=config, domain=domain, solution=solution, residual_calculator=residual_calculator, boundary_condition=boundary_condition)) + else + error("不支持的 RK 阶数: $(config.rk_order)") + end + + #@show typeof(integrator) + + # 注入 cfd 到 residual_calculator 和 integrator + residual_calculator.cfd = (config=config, domain=domain, solution=solution, reconstructor=reconstructor, residual_calculator=residual_calculator, integrator=integrator, boundary_condition=boundary_condition) + integrator.base.cfd = residual_calculator.cfd + + result = Dict{String, Any}() + new(config, domain, solution, reconstructor, residual_calculator, integrator, boundary_condition, result) + end +end + +""" +通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界 +""" +function exact_solution(cfd::Cfd) + x = cfd.domain.mesh.xcc + T = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = @. (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = if cfd.config.ic_type == "step" + StepFunctionIC(cfd.config) + elseif cfd.config.ic_type == "sin" + SineWaveIC(cfd.config) + elseif cfd.config.ic_type == "gaussian" + GaussianPulseIC(cfd.config) + else + error("未知初始条件: $(cfd.config.ic_type)") + end + + return evaluate_at(ic, x_shifted) +end + +""" +主求解循环 +""" +function run!(cfd::Cfd) + # 应用初始边界条件并同步 old field + apply!(cfd.boundary_condition, cfd.solution.u) + update_old_field(cfd.solution) + + t = 0.0 + dt_old = cfd.config.dt + dt = dt_old + + while t < cfd.config.final_time + if t + dt > cfd.config.final_time + dt = cfd.config.final_time - t + end + #@show t, dt, maximum(cfd.solution.u), minimum(cfd.solution.u) + # 执行时间步 + step(cfd.integrator, dt) + t += dt + end + + # 恢复 dt + cfd.config.dt = dt_old + + # 整理结果 + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied-1] # Python: [ist:ied] + analytical = exact_solution(cfd) + + cfd.result = Dict( + "x" => cfd.domain.mesh.xcc, + "numerical" => u_numerical, + "analytical" => analytical, + "config" => Dict( + "scheme" => cfd.config.recon_scheme, + "order" => cfd.config.spatial_order, + "rk_order" => cfd.config.rk_order, + "final_time" => cfd.config.final_time + ) + ) + + return u_numerical +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/test/test_boundary.jl b/example/1d-linear-convection/weno3/julia/01j/julia/test/test_boundary.jl new file mode 100644 index 00000000..ef27d682 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/test/test_boundary.jl @@ -0,0 +1,57 @@ +# julia/test/test_boundary.jl +using NPZ +include("../boundary.jl") + +struct MockConfig + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + debug::Bool +end + +struct MockDomain + nghosts::Int + ist::Int # Julia 本地索引(1-based) + ied::Int + ntcells::Int +end + +struct MockCfd + config::MockConfig + domain::MockDomain +end + +# ===== 关键:使用 Julia 本地索引规则 ===== +nghosts = 2 +ncells = 40 +ist = nghosts + 1 # = 3 +ied = ist + ncells # = 43 +ntcells = ncells + 2 * nghosts # = 44 + +config = MockConfig("dirichlet", 0.5, 1.5, true) +domain = MockDomain(nghosts, ist, ied, ntcells) +cfd_mock = MockCfd(config, domain) + +# 加载 Python 生成的 u_input.npy +# 注意:Python u[0] → Julia u[1],所以内容完全对应 +u_input = npzread("../../python/u_input.npy") +@assert length(u_input) == ntcells "数组长度不匹配!" + +# 测试三种边界 +test_cases = [ + ("periodic", PeriodicBoundary(cfd_mock)), + ("dirichlet", DirichletBoundary(cfd_mock)), + ("neumann", NeumannBoundary(cfd_mock)) +] + +for (name, bc) in test_cases + u = copy(u_input) + apply!(bc, u) + + u_py = npzread("../../python/u_$(name)_py.npy") + err = maximum(abs.(u .- u_py)) + println("边界: $(name) | 最大误差: $(err)") + @assert err < 1e-12 "❌ $(name) 不一致!" +end + +println("✅ 所有边界条件测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/test/test_domain.jl b/example/1d-linear-convection/weno3/julia/01j/julia/test/test_domain.jl new file mode 100644 index 00000000..7b423f77 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/test/test_domain.jl @@ -0,0 +1,44 @@ +# julia/test/test_domain.jl +include("../mesh.jl") +include("../domain.jl") + +# MockConfig:模拟 Python CfdConfig +struct MockConfig + recon_scheme::String + spatial_order::Int +end + +# 测试 ENO +config_eno = MockConfig("eno", 2) +mesh = Mesh() +domain_eno = Domain(config_eno, mesh) + +println("ENO: nghosts = ", domain_eno.nghosts) # 2 +println("ENO: ist = ", domain_eno.ist) # 2 +println("ENO: ied = ", domain_eno.ied) # 42 +println("物理索引范围: ", collect(get_physical_indices(domain_eno))[1:3], " ... ", collect(get_physical_indices(domain_eno))[end-2:end]) + +# 测试 WENO(字符串 "weno") +config_weno = MockConfig("weno", 2) +domain_weno = Domain(config_weno, mesh) +println("WENO: nghosts = ", domain_weno.nghosts) # 2 + +# 测试 WENO3(字符串 "weno3") +config_weno3 = MockConfig("weno3", 2) +domain_weno3 = Domain(config_weno3, mesh) +println("WENO3: nghosts = ", domain_weno3.nghosts) # 2 + +# 测试 is_physical_cell +@assert is_physical_cell(domain_eno, 2) == true # ist=2 +@assert is_physical_cell(domain_eno, 41) == true # ied-1=41 +@assert is_physical_cell(domain_eno, 42) == false # ied=42 + +# ✅ 断言 +@assert domain_eno.nghosts == 2 +@assert domain_eno.ist == 2 +@assert domain_eno.ied == 42 +@assert domain_eno.ntcells == 44 +@assert domain_weno.nghosts == 2 +@assert domain_weno3.nghosts == 2 + +println("✅ Domain 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/test/test_eno.jl b/example/1d-linear-convection/weno3/julia/01j/julia/test/test_eno.jl new file mode 100644 index 00000000..17b9a7a4 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/test/test_eno.jl @@ -0,0 +1,34 @@ +# julia/test/test_eno.jl +include("../mesh.jl") +include("../domain.jl") +include("../solution.jl") +include("../reconstructor/eno.jl") + +struct MockCfd + domain::Domain + solution::Solution +end + +mesh = Mesh() +# 注意:Domain 使用 1-based 索引(ist = nghosts + 1) +domain = Domain((recon_scheme="eno", spatial_order=2), mesh) +solution = Solution((ic_type="step",), domain) + +# 线性初始场: u[i] = (i-1) * 0.1 +u = solution.u +for i in 1:domain.ntcells + u[i] = Float64(i-1) * 0.1 +end + +eno = EnoReconstructor(2, domain.ntcells) +cfd = MockCfd(domain, solution) +reconstruct(eno, u, cfd) + +println("q_face_left[1] = ", solution.q_face_left[1]) +println("q_face_right[1] = ", solution.q_face_right[1]) + +# ENO2 对线性函数应精确重构 0.15 +@assert abs(solution.q_face_left[1] - 0.15) < 1e-12 +@assert abs(solution.q_face_right[1] - 0.15) < 1e-12 + +println("✅ ENO 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/test/test_flux.jl b/example/1d-linear-convection/weno3/julia/01j/julia/test/test_flux.jl new file mode 100644 index 00000000..0ebc1dc0 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/test/test_flux.jl @@ -0,0 +1,53 @@ +# julia/test/test_flux.jl +include("../mesh.jl") +include("../flux.jl") + +# Mock 对象 +struct MockConfig + flux_type::String + wave_speed::Float64 +end + +struct MockDomain + mesh::Mesh +end + +struct MockCfd + config::MockConfig + domain::MockDomain +end + +# 创建 mock +mesh = Mesh() +config = MockConfig("rusanov", 1.0) +domain = MockDomain(mesh) +cfd = MockCfd(config, domain) + +# 测试 Rusanov +rusanov = RusanovFluxCalculator(cfd) +N = mesh.nnodes +qL = [1.0, 2.0, 3.0, 4.0, 5.0, zeros(Float64, N-5)...] +qR = [2.0, 3.0, 4.0, 5.0, 6.0, zeros(Float64, N-5)...] +flux_rus = zeros(Float64, N) +compute!(rusanov, qL, qR, flux_rus) + +println("Rusanov flux[1] = ", flux_rus[1]) # 应为 1.0 +@assert abs(flux_rus[1] - 1.0) < 1e-12 + +# 测试 Engquist-Osher +eo = EngquistOsherFluxCalculator(cfd) +flux_eo = zeros(Float64, N) +compute!(eo, qL, qR, flux_eo) + +println("EO flux[1] = ", flux_eo[1]) # c=1 → cp=1, cm=0 → flux=1*1 + 0*2 = 1.0 +@assert abs(flux_eo[1] - 1.0) < 1e-12 + +# 测试 c = -1.0 +config_neg = MockConfig("rusanov", -1.0) +cfd_neg = MockCfd(config_neg, domain) +rusanov_neg = RusanovFluxCalculator(cfd_neg) +compute!(rusanov_neg, qL, qR, flux_rus) +println("Rusanov (c=-1) flux[1] = ", flux_rus[1]) # F_L=-1, F_R=-2, Smax=1 → flux = -1.5 -0.5*(-1) = -1.0 +@assert abs(flux_rus[1] - (-2.0)) < 1e-12 # ← 修正:-2.0 而非 -1.0 + +println("✅ Flux 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/test/test_initial_condition.jl b/example/1d-linear-convection/weno3/julia/01j/julia/test/test_initial_condition.jl new file mode 100644 index 00000000..dc58691a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/test/test_initial_condition.jl @@ -0,0 +1,45 @@ +# julia/test/test_initial_condition.jl +using NPZ + +# 包含初始条件模块 +include("../initial_condition.jl") + +# 构造 mock config(与 Python 完全一致) +struct MockConfig + ic_type::String + domain_length::Float64 + pulse_center::Float64 + pulse_width::Float64 +end + +# 生成与 Python Mesh.xcc 完全相同的 x 坐标 +function generate_xcc() + xmin, xmax = 0.0, 2.0 + ncells = 40 + dx = (xmax - xmin) / ncells + xcc = Vector{Float64}(undef, ncells) + for i in 1:ncells + xcc[i] = xmin + (i - 0.5) * dx # i-1 + 0.5 → i-0.5 + end + return xcc +end + +# 主测试 +xcc = generate_xcc() + +test_cases = [ + ("step", StepFunctionIC(MockConfig("step", 2.0, 0.5, 0.1))), + ("sin", SineWaveIC(MockConfig("sin", 2.0, 0.5, 0.1))), + ("gaussian", GaussianPulseIC(MockConfig("gaussian", 2.0, 0.5, 0.1))) +] + +for (name, ic) in test_cases + u_jl = evaluate_at(ic, xcc) + u_py = npzread("../../python/u_$(name)_interior_py.npy") + + err = maximum(abs.(u_jl .- u_py)) + println("IC: $(name) | 最大误差: $(err)") + @assert err < 1e-12 "❌ $(name) 不一致!" +end + +println("✅ 所有初始条件测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/test/test_mesh.jl b/example/1d-linear-convection/weno3/julia/01j/julia/test/test_mesh.jl new file mode 100644 index 00000000..315d2aac --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/test/test_mesh.jl @@ -0,0 +1,37 @@ +# julia/test/test_mesh.jl +include("../mesh.jl") + +# 创建 mesh(与 Python 完全相同) +mesh = Mesh() + +# 打印关键值(与 Python 对比) +println("xmin = ", mesh.xmin) # 0.0 +println("xmax = ", mesh.xmax) # 2.0 +println("ncells = ", mesh.ncells) # 40 +println("nnodes = ", mesh.nnodes) # 41 +println("nx = ", mesh.nx) # 40 +println("L = ", mesh.L) # 2.0 +println("dx = ", mesh.dx) # 0.05 + +# 检查 x[1] (Python x[0]) 和 x[41] (Python x[40]) +println("x[1] = ", mesh.x[1]) # 0.0 +println("x[41] = ", mesh.x[41]) # 2.0 + +# 检查 xcc[1] (Python xcc[0]) 和 xcc[40] (Python xcc[39]) +println("xcc[1] = ", mesh.xcc[1]) # 0.025 +println("xcc[40] = ", mesh.xcc[40]) # 1.975 + +# ✅ 严格断言 +@assert mesh.xmin == 0.0 +@assert mesh.xmax == 2.0 +@assert mesh.ncells == 40 +@assert mesh.nnodes == 41 +@assert mesh.nx == 40 +@assert mesh.L == 2.0 +@assert mesh.dx == 0.05 +@assert mesh.x[1] == 0.0 +@assert mesh.x[41] == 2.0 +@assert abs(mesh.xcc[1] - 0.025) < 1e-12 +@assert abs(mesh.xcc[40] - 1.975) < 1e-12 + +println("✅ Mesh 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/test/test_residual.jl b/example/1d-linear-convection/weno3/julia/01j/julia/test/test_residual.jl new file mode 100644 index 00000000..e37e8d2b --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/test/test_residual.jl @@ -0,0 +1,53 @@ +# julia/test/test_residual.jl +include("../mesh.jl") +include("../domain.jl") +include("../solution.jl") +include("../flux.jl") +include("../residual.jl") + +# ===== Dummy Reconstructor ===== +struct DummyReconstructor end + +# ===== Mock Config ===== +struct MockConfig + flux_type::String + wave_speed::Float64 +end + +# ===== Mock CFD (必须包含 reconstructor 字段) ===== +struct MockCfd + config::MockConfig + domain::Domain + solution::Solution + reconstructor::DummyReconstructor # ← 关键:添加此字段 +end + +# ===== 主测试 ===== +config = MockConfig("rusanov", 1.0) +mesh = Mesh() +domain = Domain((recon_scheme="eno", spatial_order=2), mesh) +solution = Solution((ic_type="step",), domain) + +# 手动设置界面值(跳过重建) +for i in 1:mesh.nnodes + solution.q_face_left[i] = Float64(i) * 0.1 + solution.q_face_right[i] = Float64(i) * 0.1 +end + +flux_calc = RusanovFluxCalculator((config=config, domain=domain)) +dummy_recon = DummyReconstructor() +cfd = MockCfd(config, domain, solution, dummy_recon) + +# 创建残差计算器 +res_calc = ResidualCalculator(cfd, flux_calc) + +# 计算残差(注意:_reconstruct 仍被注释) +compute!(res_calc) + +println("flux[1] = ", solution.flux[1]) +println("res[1] = ", solution.res[1]) + +@assert abs(solution.flux[1] - 0.1) < 1e-12 +@assert abs(solution.res[1] - (-2.0)) < 1e-12 + +println("✅ Residual 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/test/test_solution.jl b/example/1d-linear-convection/weno3/julia/01j/julia/test/test_solution.jl new file mode 100644 index 00000000..f44ff0f0 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/test/test_solution.jl @@ -0,0 +1,42 @@ +# julia/test/test_solution.jl +include("../mesh.jl") +include("../domain.jl") +include("../solution.jl") + +# MockConfig +struct MockConfig + recon_scheme::String + spatial_order::Int + ic_type::String + domain_length::Float64 + pulse_center::Float64 + pulse_width::Float64 +end + +# 创建 solution +config = MockConfig("eno", 2, "step", 2.0, 0.5, 0.1) +mesh = Mesh() +domain = Domain(config, mesh) +sol = Solution(config, domain) + +# 检查字段尺寸 +@assert length(sol.q_face_left) == mesh.nnodes # 41 +@assert length(sol.flux) == mesh.nnodes # 41 +@assert length(sol.res) == mesh.ncells # 40 +@assert length(sol.u) == domain.ntcells # 44 + +# 检查初始场 +println("u[3] (物理起始): ", sol.u[3]) # 应为 1.0 +println("u[23] (x=1.0): ", sol.u[23]) # 应为 2.0 + +# 测试 update_old_field +sol.u[3] = 999.0 +update_old_field(sol) +@assert sol.un[3] == 999.0 + +# 测试 reset_solution +reset_solution(sol) +@assert sol.u[3] == 0.0 +@assert sol.un[3] == 0.0 + +println("✅ Solution 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/test/test_time_integration.jl b/example/1d-linear-convection/weno3/julia/01j/julia/test/test_time_integration.jl new file mode 100644 index 00000000..091a8637 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/test/test_time_integration.jl @@ -0,0 +1,53 @@ +# julia/test/test_time_integration.jl +include("../time_integration.jl") +include("../flux.jl") +include("../boundary.jl") + +# Mock CFD 对象 +struct MockCfd + config::Any + domain::Domain + solution::Solution + residual_calculator::Any + boundary_condition::Any +end + +# 创建完整链路 +mesh = Mesh() +domain = Domain((recon_scheme="eno", spatial_order=2), mesh) +solution = Solution((ic_type="step",), domain) + +# 手动设置界面值(跳过 reconstructor) +for i in 1:mesh.nnodes + solution.q_face_left[i] = Float64(i) * 0.1 + solution.q_face_right[i] = Float64(i) * 0.1 +end + +# Mock components +flux_calc = RusanovFluxCalculator((config=(wave_speed=1.0,), domain=domain)) +res_calc = ResidualCalculator((config=nothing, domain=domain, solution=solution, reconstructor=nothing), flux_calc) +bc = PeriodicBoundary((domain=domain, config=(debug=false,))) +cfd = MockCfd((rk_order=1,), domain, solution, res_calc, bc) + +# 测试 RK1 +rk1 = RK1Integrator(cfd) +step(rk1, 0.025) + +println("RK1 step completed") +@assert solution.u[3] != 0.0 # 应已更新 + +# 测试 RK2 +cfd2 = MockCfd((rk_order=2,), domain, solution, res_calc, bc) +rk2 = RK2Integrator(cfd2) +step(rk2, 0.025) + +println("RK2 step completed") + +# 测试 RK3 +cfd3 = MockCfd((rk_order=3,), domain, solution, res_calc, bc) +rk3 = RK3Integrator(cfd3) +step(rk3, 0.025) + +println("RK3 step completed") + +println("✅ Time Integration 逻辑测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/test/test_weno3.jl b/example/1d-linear-convection/weno3/julia/01j/julia/test/test_weno3.jl new file mode 100644 index 00000000..9a91761e --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/test/test_weno3.jl @@ -0,0 +1,47 @@ +# julia/test/test_weno3.jl +include("../mesh.jl") +include("../domain.jl") +include("../solution.jl") +include("../reconstructor/weno3.jl") + +# Mock CFD +struct MockCfd + domain::Domain + solution::Solution +end + +# 创建数据 +mesh = Mesh() +# 注意:Domain 现在使用 1-based 索引(ist = nghosts + 1) +domain = Domain((recon_scheme="weno3", spatial_order=2), mesh) +solution = Solution((ic_type="step",), domain) + +# 设置 u(物理区域 + ghost) +u = solution.u +for i in 1:domain.ntcells + u[i] = Float64(i-1) * 0.1 # u = [0.0, 0.1, 0.2, ..., 4.3] +end + +# 创建 reconstructor +weno3 = Weno3Reconstructor() +cfd = MockCfd(domain, solution) + +# 重建 +reconstruct(weno3, u, cfd) + +println("q_face_left length = ", length(solution.q_face_left)) # 应为 41 +println("q_face_right length = ", length(solution.q_face_right)) # 应为 41 +println("q_face_left[1] = ", solution.q_face_left[1]) +println("q_face_right[1] = ", solution.q_face_right[1]) + +# 验证左界面值(i = ist-1 = 2, j = 1) +# v1 = u[1] = 0.0, v2 = u[2] = 0.1, v3 = u[3] = 0.2 +# qL = w0*(-0.5*0.0 + 1.5*0.1) + w1*(0.5*0.1 + 0.5*0.2) = w0*0.15 + w1*0.15 = 0.15 +@assert abs(solution.q_face_left[1] - 0.15) < 1e-12 + +# 验证右界面值(i = ist = 3, j = 1) +# v1 = u[2] = 0.1, v2 = u[3] = 0.2, v3 = u[4] = 0.3 +# qR = w0*(0.5*0.1 + 0.5*0.2) + w1*(1.5*0.2 - 0.5*0.3) = w0*0.15 + w1*0.15 = 0.15 +@assert abs(solution.q_face_right[1] - 0.15) < 1e-12 + +println("✅ WENO3 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/test_residual.jl b/example/1d-linear-convection/weno3/julia/01j/julia/test_residual.jl new file mode 100644 index 00000000..315d2aac --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/test_residual.jl @@ -0,0 +1,37 @@ +# julia/test/test_mesh.jl +include("../mesh.jl") + +# 创建 mesh(与 Python 完全相同) +mesh = Mesh() + +# 打印关键值(与 Python 对比) +println("xmin = ", mesh.xmin) # 0.0 +println("xmax = ", mesh.xmax) # 2.0 +println("ncells = ", mesh.ncells) # 40 +println("nnodes = ", mesh.nnodes) # 41 +println("nx = ", mesh.nx) # 40 +println("L = ", mesh.L) # 2.0 +println("dx = ", mesh.dx) # 0.05 + +# 检查 x[1] (Python x[0]) 和 x[41] (Python x[40]) +println("x[1] = ", mesh.x[1]) # 0.0 +println("x[41] = ", mesh.x[41]) # 2.0 + +# 检查 xcc[1] (Python xcc[0]) 和 xcc[40] (Python xcc[39]) +println("xcc[1] = ", mesh.xcc[1]) # 0.025 +println("xcc[40] = ", mesh.xcc[40]) # 1.975 + +# ✅ 严格断言 +@assert mesh.xmin == 0.0 +@assert mesh.xmax == 2.0 +@assert mesh.ncells == 40 +@assert mesh.nnodes == 41 +@assert mesh.nx == 40 +@assert mesh.L == 2.0 +@assert mesh.dx == 0.05 +@assert mesh.x[1] == 0.0 +@assert mesh.x[41] == 2.0 +@assert abs(mesh.xcc[1] - 0.025) < 1e-12 +@assert abs(mesh.xcc[40] - 1.975) < 1e-12 + +println("✅ Mesh 测试通过!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/julia/time_integration.jl b/example/1d-linear-convection/weno3/julia/01j/julia/time_integration.jl new file mode 100644 index 00000000..95e5cd67 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/julia/time_integration.jl @@ -0,0 +1,130 @@ +# julia/time_integration.jl +""" +时间推进器模块(与 time_integration.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("residual.jl") +include("boundary.jl") + +# ---------------------- 抽象时间推进器基类 ---------------------- +abstract type TimeIntegrator end + +mutable struct TimeIntegratorBase <: TimeIntegrator + cfd::Any + config::Any + domain::Domain + solution::Solution + residual_calculator::Any # ResidualCalculator +end + +function TimeIntegratorBase(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + residual_calculator = cfd.residual_calculator + TimeIntegratorBase(cfd, config, domain, solution, residual_calculator) +end + +function compute_residual(integrator::TimeIntegratorBase) + compute!(integrator.residual_calculator) +end + +function apply_boundary(integrator::TimeIntegratorBase) + apply!(integrator.cfd.boundary_condition, integrator.solution.u) +end + +function map_idx(integrator::TimeIntegratorBase, i::Int) + return i - integrator.domain.ist + 1 # ← +1 转为 1-based +end + +# ---------------------- RK1Integrator ---------------------- +mutable struct RK1Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK1Integrator(cfd::Any) + RK1Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK1Integrator, dt::Float64) + compute_residual(integrator.base) + for i in integrator.base.domain.ist:(integrator.base.domain.ied - 1) + j = map_idx(integrator.base, i) + integrator.base.solution.u[i] += dt * integrator.base.solution.res[j] + end + apply_boundary(integrator.base) + update_old_field(integrator.base.solution) +end + +# ---------------------- RK2Integrator ---------------------- +mutable struct RK2Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK2Integrator(cfd::Any) + RK2Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK2Integrator, dt::Float64) + base = integrator.base + # 阶段1:预测步 + compute_residual(base) + u_pred = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u_pred[i] += dt * base.solution.res[j] + end + base.solution.u .= u_pred + apply_boundary(base) + # 阶段2:校正步 + compute_residual(base) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = 0.5 * base.solution.un[i] + 0.5 * base.solution.u[i] + 0.5 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- RK3Integrator ---------------------- +mutable struct RK3Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK3Integrator(cfd::Any) + RK3Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK3Integrator, dt::Float64) + base = integrator.base + # 阶段1 + compute_residual(base) + u1 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u1[i] += dt * base.solution.res[j] + end + base.solution.u .= u1 + apply_boundary(base) + # 阶段2 + compute_residual(base) + u2 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u2[i] = 0.75 * base.solution.un[i] + 0.25 * base.solution.u[i] + 0.25 * dt * base.solution.res[j] + end + base.solution.u .= u2 + apply_boundary(base) + # 阶段3 + compute_residual(base) + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = c1 * base.solution.un[i] + c2 * base.solution.u[i] + c3 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/python/boundary.py b/example/1d-linear-convection/weno3/julia/01j/python/boundary.py new file mode 100644 index 00000000..6054f92d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/python/boundary.py @@ -0,0 +1,103 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/python/cfd_registry.py b/example/1d-linear-convection/weno3/julia/01j/python/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/python/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/python/config.py b/example/1d-linear-convection/weno3/julia/01j/python/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/python/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/python/domain.py b/example/1d-linear-convection/weno3/julia/01j/python/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/python/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/python/eno_weno_comparison.png b/example/1d-linear-convection/weno3/julia/01j/python/eno_weno_comparison.png new file mode 100644 index 00000000..8d7135ec Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01j/python/eno_weno_comparison.png differ diff --git a/example/1d-linear-convection/weno3/julia/01j/python/factories/base_factory.py b/example/1d-linear-convection/weno3/julia/01j/python/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/python/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/python/flux.py b/example/1d-linear-convection/weno3/julia/01j/python/flux.py new file mode 100644 index 00000000..5ac73aa8 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/python/flux.py @@ -0,0 +1,74 @@ +# flux.py +""" +通量计算器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册,替代硬编码工厂 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象通量计算基类(统一接口) ---------------------- +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass + +# ---------------------- 2. 具体通量计算子类(使用装饰器注册) ---------------------- + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + +class FluxCalculatorFactory: + """通量计算器工厂""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD对象 + + Returns: + 通量计算器实例 + """ + from factories.base_factory import BaseFactory + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/python/gen_boundary_test_data.py b/example/1d-linear-convection/weno3/julia/01j/python/gen_boundary_test_data.py new file mode 100644 index 00000000..c7fc2a9c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/python/gen_boundary_test_data.py @@ -0,0 +1,35 @@ +# python/gen_boundary_test_data.py +import numpy as np +from config import CfdConfig +from mesh import Mesh +from domain import Domain + +# 固定测试配置 +config = CfdConfig() +config.with_boundary("dirichlet", left_value=0.5, right_value=1.5) +config.debug = True + +mesh = Mesh() +domain = Domain(config, mesh) + +# 构造 mock CFD 对象(仅含 config + domain) +class MockCfd: + def __init__(self, config, domain): + self.config = config + self.domain = domain + +# 测试用 u:0,1,2,...,N-1 +u_input = np.arange(domain.ntcells, dtype=np.float64) +np.save("u_input.npy", u_input) + +# 测试每种边界 +from boundary import PeriodicBoundary, DirichletBoundary, NeumannBoundary + +for bc_name, bc_class in [("periodic", PeriodicBoundary), ("dirichlet", DirichletBoundary), ("neumann", NeumannBoundary)]: + u = u_input.copy() + cfd_mock = MockCfd(config, domain) + bc = bc_class(cfd_mock) + bc.apply(u) + np.save(f"u_{bc_name}_py.npy", u) + +print("✅ 测试数据已生成:u_*.npy") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/python/gen_ic_test_data.py b/example/1d-linear-convection/weno3/julia/01j/python/gen_ic_test_data.py new file mode 100644 index 00000000..69c2573b --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/python/gen_ic_test_data.py @@ -0,0 +1,27 @@ +# python/gen_ic_test_data.py +import sys, os +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +import numpy as np +from config import CfdConfig +from mesh import Mesh +from domain import Domain +from solution import Solution + +# 固定 mesh +mesh = Mesh() +config = CfdConfig() + +# 测试三种 IC +for ic_type in ["step", "sin", "gaussian"]: + config.ic_type = ic_type + domain = Domain(config, mesh) + sol = Solution(config, domain) + + u_full = sol.u.copy() # 包含 ghost + u_interior = sol.u[domain.ist:domain.ied].copy() + + np.save(f"u_{ic_type}_full_py.npy", u_full) + np.save(f"u_{ic_type}_interior_py.npy", u_interior) + +print("✅ 初始条件测试数据已生成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/python/initial_condition.py b/example/1d-linear-convection/weno3/julia/01j/python/initial_condition.py new file mode 100644 index 00000000..047415b7 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/python/initial_condition.py @@ -0,0 +1,90 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, ic_type: str, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', ic_type, config) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/python/mesh.py b/example/1d-linear-convection/weno3/julia/01j/python/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/python/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/python/plotter.py b/example/1d-linear-convection/weno3/julia/01j/python/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/python/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/python/reconstructor/__init__.py b/example/1d-linear-convection/weno3/julia/01j/python/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/python/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/python/reconstructor/base.py b/example/1d-linear-convection/weno3/julia/01j/python/reconstructor/base.py new file mode 100644 index 00000000..bbd63850 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/python/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def reconstruct(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/python/reconstructor/eno.py b/example/1d-linear-convection/weno3/julia/01j/python/reconstructor/eno.py new file mode 100644 index 00000000..c2fb385d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/python/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def reconstruct(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/python/reconstructor/factory.py b/example/1d-linear-convection/weno3/julia/01j/python/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/python/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/python/reconstructor/weno3.py b/example/1d-linear-convection/weno3/julia/01j/python/reconstructor/weno3.py new file mode 100644 index 00000000..bf68be50 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/python/reconstructor/weno3.py @@ -0,0 +1,59 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def reconstruct(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/julia/01j/python/registry.py b/example/1d-linear-convection/weno3/julia/01j/python/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/python/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/python/residual.py b/example/1d-linear-convection/weno3/julia/01j/python/residual.py new file mode 100644 index 00000000..b4d4d7dc --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/python/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._reconstruct() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _reconstruct(self): + """私有方法:界面值重建""" + self.reconstructor.reconstruct(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/python/run_eno_weno.py b/example/1d-linear-convection/weno3/julia/01j/python/run_eno_weno.py new file mode 100644 index 00000000..ff46f226 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/python/run_eno_weno.py @@ -0,0 +1,54 @@ +# run_eno_weno.py + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/python/solution.py b/example/1d-linear-convection/weno3/julia/01j/python/solution.py new file mode 100644 index 00000000..92a46d3f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/python/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config.ic_type, config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/python/solver.py b/example/1d-linear-convection/weno3/julia/01j/python/solver.py new file mode 100644 index 00000000..fdb5e46a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/python/solver.py @@ -0,0 +1,85 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + + +from flux import InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory + +from boundary import BoundaryCondition, PeriodicBoundary, DirichletBoundary, NeumannBoundary, BoundaryConditionFactory + +from time_integration import TimeIntegrator, RK1Integrator, RK2Integrator, RK3Integrator, TimeIntegratorFactory + +from mesh import Mesh + +from reconstructor import ReconstructorFactory + +from initial_condition import InitialCondition, StepFunctionIC, SineWaveIC, GaussianPulseIC, InitialConditionFactory + +from domain import Domain +from solution import Solution + +from config import CfdConfig +from residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config.ic_type, self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/julia/01j/python/time_integration.py b/example/1d-linear-convection/weno3/julia/01j/python/time_integration.py new file mode 100644 index 00000000..25ac0b4c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/01j/python/time_integration.py @@ -0,0 +1,125 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class TimeIntegratorFactory: + """时间推进器工厂""" + + @staticmethod + def create(cfd) -> 'TimeIntegrator': + """ + 创建时间推进器实例 + + Args: + cfd: CFD对象 + + Returns: + 时间推进器实例 + """ + from factories.base_factory import BaseFactory + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/01j/python/u_dirichlet_py.npy b/example/1d-linear-convection/weno3/julia/01j/python/u_dirichlet_py.npy new file mode 100644 index 00000000..3aa20bd2 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01j/python/u_dirichlet_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01j/python/u_gaussian_full_py.npy b/example/1d-linear-convection/weno3/julia/01j/python/u_gaussian_full_py.npy new file mode 100644 index 00000000..b762f0fe Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01j/python/u_gaussian_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01j/python/u_gaussian_interior_py.npy b/example/1d-linear-convection/weno3/julia/01j/python/u_gaussian_interior_py.npy new file mode 100644 index 00000000..68af8954 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01j/python/u_gaussian_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01j/python/u_input.npy b/example/1d-linear-convection/weno3/julia/01j/python/u_input.npy new file mode 100644 index 00000000..ef506fa7 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01j/python/u_input.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01j/python/u_neumann_py.npy b/example/1d-linear-convection/weno3/julia/01j/python/u_neumann_py.npy new file mode 100644 index 00000000..fa723d1e Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01j/python/u_neumann_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01j/python/u_periodic_py.npy b/example/1d-linear-convection/weno3/julia/01j/python/u_periodic_py.npy new file mode 100644 index 00000000..156aff85 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01j/python/u_periodic_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01j/python/u_sin_full_py.npy b/example/1d-linear-convection/weno3/julia/01j/python/u_sin_full_py.npy new file mode 100644 index 00000000..b87f8a13 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01j/python/u_sin_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01j/python/u_sin_interior_py.npy b/example/1d-linear-convection/weno3/julia/01j/python/u_sin_interior_py.npy new file mode 100644 index 00000000..6803fbfb Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01j/python/u_sin_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01j/python/u_step_full_py.npy b/example/1d-linear-convection/weno3/julia/01j/python/u_step_full_py.npy new file mode 100644 index 00000000..2fc0e183 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01j/python/u_step_full_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/01j/python/u_step_interior_py.npy b/example/1d-linear-convection/weno3/julia/01j/python/u_step_interior_py.npy new file mode 100644 index 00000000..b5ad6600 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/01j/python/u_step_interior_py.npy differ diff --git a/example/1d-linear-convection/weno3/julia/02/julia/boundary.jl b/example/1d-linear-convection/weno3/julia/02/julia/boundary.jl new file mode 100644 index 00000000..5b0baaf4 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/julia/boundary.jl @@ -0,0 +1,90 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/julia/config.jl b/example/1d-linear-convection/weno3/julia/02/julia/config.jl new file mode 100644 index 00000000..bf48de3a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/julia/config.jl @@ -0,0 +1,68 @@ +# julia/config.jl +""" +CfdConfig:与 Python config.py 完全同构 +""" +mutable struct CfdConfig + ic_type::String + recon_scheme::String + flux_type::String + rk_order::Int + wave_speed::Float64 + final_time::Float64 + dt::Float64 + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + spatial_order::Int + + function CfdConfig() + new( + "step", + "eno", + "rusanov", + 1, + 1.0, + 0.625, + 0.025, + "periodic", + 1.0, + 2.0, + 2 + ) + end +end + +""" +专用配置:重建方案(链式调用) +""" +function with_reconstruction(cfg::CfdConfig, scheme::String, order::Union{Int, Nothing}=nothing) + cfg.recon_scheme = lowercase(scheme) + + if order !== nothing + cfg.spatial_order = order + else + if startswith(cfg.recon_scheme, "weno") + cfg.spatial_order = 5 + elseif cfg.recon_scheme == "eno" + cfg.spatial_order = 3 + else + error("不支持的重建格式:$scheme(仅支持 eno/weno)") + end + end + + return cfg # 支持链式调用 +end + +""" +专用配置:边界条件(链式调用) +""" +function with_boundary(cfg::CfdConfig, bc_type::String; left_value=nothing, right_value=nothing) + cfg.boundary_type = bc_type + if left_value !== nothing + cfg.left_boundary_value = left_value + end + if right_value !== nothing + cfg.right_boundary_value = right_value + end + return cfg +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/julia/domain.jl b/example/1d-linear-convection/weno3/julia/02/julia/domain.jl new file mode 100644 index 00000000..a7edc226 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/julia/domain.jl @@ -0,0 +1,61 @@ +# julia/domain.jl +""" +Domain 结构体(与 domain.py 完全同构) +- 保存 config +- nghosts 计算逻辑 1:1 +- ist/ied 为 0-based(与 Python 一致) +""" + +include("mesh.jl") + +mutable struct Domain + config::Any # 保存 config(与 Python 一致) + mesh::Mesh + nghosts::Int + ist::Int # 0-based + ied::Int # 0-based + ntcells::Int +end + +function Domain(config::Any, mesh::Mesh) + nghosts = _calc_nghosts(config) + ist = nghosts + 1 # ← 1-based 起始索引 + ied = ist + mesh.ncells # ← 1-based 结束索引(不包含) + ntcells = mesh.ncells + 2 * nghosts + Domain(config, mesh, nghosts, ist, ied, ntcells) +end + +function _calc_nghosts(config::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + order = getfield_safe(config, :spatial_order, 2) + + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + if scheme == "eno" + nghosts = order + elseif startswith(scheme, "weno") + nghosts = order ÷ 2 + 1 + else + error("未知重建格式 $(scheme),无法计算ghost层!") + end + + if nghosts <= 0 + error("计算得到的ghost层数量无效:$(nghosts)(阶数$(order),格式$(scheme))") + end + + return nghosts +end + +function is_physical_cell(domain::Domain, idx::Int) + return domain.ist <= idx < domain.ied +end + +function get_physical_indices(domain::Domain) + return domain.ist:(domain.ied - 1) +end + +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/julia/flux.jl b/example/1d-linear-convection/weno3/julia/02/julia/flux.jl new file mode 100644 index 00000000..047598f7 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/julia/flux.jl @@ -0,0 +1,70 @@ +# julia/flux.jl +""" +通量计算器模块(与 flux.py 完全同构) +- 抽象基类 + 具体实现 +- 字段:cfd, config, mesh, wave_speed +""" + +include("mesh.jl") + +# ---------------------- 抽象基类 ---------------------- +""" +InviscidFluxCalculator 抽象类型 +Julia 无 ABC,用文档约定 +所有子类型必须实现 compute! +""" +abstract type InviscidFluxCalculator end + +# ---------------------- RusanovFluxCalculator ---------------------- +mutable struct RusanovFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function RusanovFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::RusanovFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = calc.wave_speed + c_R = calc.wave_speed + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + end +end + +# ---------------------- EngquistOsherFluxCalculator ---------------------- +mutable struct EngquistOsherFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function EngquistOsherFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::EngquistOsherFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + c = calc.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/julia/initial_condition.jl b/example/1d-linear-convection/weno3/julia/02/julia/initial_condition.jl new file mode 100644 index 00000000..5e029e8d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/julia/initial_condition.jl @@ -0,0 +1,86 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/julia/mesh.jl b/example/1d-linear-convection/weno3/julia/02/julia/mesh.jl new file mode 100644 index 00000000..1b0dd4bf --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/julia/mesh.jl @@ -0,0 +1,45 @@ +# julia/mesh.jl +""" +Mesh 结构体(与提供的 mesh.py 完全同构) +- 字段名、初始化顺序、循环逻辑完全一致 +- 不做任何泛化或优化 +""" + +mutable struct Mesh + xmin::Float64 + xmax::Float64 + ncells::Int + nnodes::Int + nx::Int + x::Vector{Float64} + xcc::Vector{Float64} + L::Float64 # 在 init_mesh 中赋值 + dx::Float64 # 在 init_mesh 中赋值 + + function Mesh() + # 与 Python __init__ 完全一致 + xmin = 0.0 + xmax = 2.0 + ncells = 40 + nnodes = ncells + 1 + nx = ncells + x = zeros(Float64, nnodes) + xcc = zeros(Float64, ncells) + + # 调用 init_mesh(模拟 Python) + L = xmax - xmin + dx = L / ncells + + # Generate node coordinates: for i in range(self.nnodes) + for i in 1:nnodes + x[i] = xmin + (i - 1) * dx # Python i → Julia i-1 + end + + # Generate cell center coordinates + for i in 1:ncells + xcc[i] = 0.5 * (x[i] + x[i+1]) + end + + new(xmin, xmax, ncells, nnodes, nx, x, xcc, L, dx) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/julia/plotter.jl b/example/1d-linear-convection/weno3/julia/02/julia/plotter.jl new file mode 100644 index 00000000..a77d8d68 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/julia/plotter.jl @@ -0,0 +1,147 @@ +# julia/plotter.jl +""" +CFDPlotter 的 Julia 实现(通过 PythonCall.jl 调用 Matplotlib) +确保与 Python plotter.py 行为完全一致 +""" + +using PythonCall + +# 初始化 Python 环境(加载 matplotlib, inflect) +const plt = pyimport("matplotlib.pyplot") +const inflect = pyimport("inflect") + +mutable struct CFDPlotter + default_styles::Dict{String, Any} + p::Py +end + +function CFDPlotter() + default_styles = Dict{String, Any}( + "numerical" => Dict( + :color => "blue", + :linestyle => "-", + :marker => "o", + :markerfacecolor => "none" + ), + "analytical" => Dict( + :color => "red", + :linestyle => "--", + :marker => "", + :linewidth => 1.5 + ), + "comparison" => [ + Dict(:color => "black", :linestyle => "-", :marker => "o", :markerfacecolor => "none"), + Dict(:color => "blue", :linestyle => "--", :marker => "s", :markerfacecolor => "none"), + Dict(:color => "green", :linestyle => ":", :marker => "^", :markerfacecolor => "none") + ] + ) + p = inflect.engine() + CFDPlotter(default_styles, p) +end + +""" +轻量即时绘图(快速验证结果) +""" +function plot_quick(plotter::CFDPlotter, cfd_result::Dict; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + rk_order = cfd_result["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = cfd_result["config"]["final_time"] + order = cfd_result["config"]["order"] + scheme = uppercase(cfd_result["config"]["scheme"]) + actual_title = "1D Convection (t=$(final_time))\n$(order)th-order $(scheme) + $(rk_str)-order RK" + end + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"]; + label="Numerical ($(uppercase(cfd_result["config"]["scheme"])))", + plotter.default_styles["numerical"]..., + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + # 通用样式 + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +""" +多格式/多精度对比绘图 +""" +function plot_comparison(plotter::CFDPlotter, result_list::Vector{Dict{String, Any}}; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + schemes = [uppercase(r["config"]["scheme"]) * string(r["config"]["order"]) for r in result_list] + rk_order = result_list[1]["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = result_list[1]["config"]["final_time"] + actual_title = "1D Convection Comparison (t=$(final_time))\n$(join(schemes, ", ")) + $(rk_str)-order RK" + end + + # 绘制多个数值解 + for (i, res) in enumerate(result_list) + style = plotter.default_styles["comparison"][mod1(i, length(plotter.default_styles["comparison"]))] + label = "Numerical ($(uppercase(res["config"]["scheme"]))$(res["config"]["order"]))" + plt.plot( + res["x"], res["numerical"]; + label=label, + style..., + markersize=5, linewidth=0.5 + ) + end + + # 绘制解析解 + plt.plot( + result_list[1]["x"], result_list[1]["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +function _set_common_style(plotter::CFDPlotter, title::String) + plt.title(title, fontsize=12) + plt.xlabel("x", fontsize=10) + plt.ylabel("u", fontsize=10) + plt.legend(fontsize=9) + plt.grid(true, color="gray", linestyle="--", linewidth=0.5, alpha=0.7) + plt.tight_layout() +end + +""" +快捷函数:ENO/WENO对比绘图 +""" +function plot_eno_weno_comparison(eno_result::Dict, weno_result::Dict; save_path=nothing) + plotter = CFDPlotter() + plot_comparison(plotter, [eno_result, weno_result]; save_path=save_path) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/julia/reconstructor/eno.jl b/example/1d-linear-convection/weno3/julia/02/julia/reconstructor/eno.jl new file mode 100644 index 00000000..e78a636f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/julia/reconstructor/eno.jl @@ -0,0 +1,107 @@ +# julia/reconstructor/eno.jl +""" +ENO 重构器(与 reconstructor/eno.py 完全同构) +""" + +# ---------------------- ENO 系数初始化 ---------------------- +function _init_eno_coef!(spatial_order::Int, coef::Matrix{Float64}) + if spatial_order == 1 + coef[1, 1] = 1.0 + coef[2, 1] = 1.0 + elseif spatial_order == 2 + coef[1, 1:2] = [3.0/2.0, -1.0/2.0] + coef[2, 1:2] = [1.0/2.0, 1.0/2.0] + coef[3, 1:2] = [-1.0/2.0, 3.0/2.0] + elseif spatial_order == 3 + coef[1, 1:3] = [11.0/6.0, -7.0/6.0, 1.0/3.0] + coef[2, 1:3] = [1.0/3.0, 5.0/6.0, -1.0/6.0] + coef[3, 1:3] = [-1.0/6.0, 5.0/6.0, 1.0/3.0] + coef[4, 1:3] = [1.0/3.0, -7.0/6.0, 11.0/6.0] + elseif spatial_order == 4 + coef[1, 1:4] = [25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0] + coef[2, 1:4] = [1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0] + coef[3, 1:4] = [-1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0] + coef[4, 1:4] = [1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0] + coef[5, 1:4] = [-1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0] + elseif spatial_order == 5 + coef[1, 1:5] = [137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0] + coef[2, 1:5] = [1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0] + coef[3, 1:5] = [-1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0] + coef[4, 1:5] = [1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0] + coef[5, 1:5] = [-1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0] + coef[6, 1:5] = [1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0] + elseif spatial_order == 6 + coef[1, 1:6] = [49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0] + coef[2, 1:6] = [1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0] + coef[3, 1:6] = [-1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0] + coef[4, 1:6] = [1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0] + coef[5, 1:6] = [-1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0] + coef[6, 1:6] = [1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0] + coef[7, 1:6] = [-1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0] + elseif spatial_order == 7 + coef[1, 1:7] = [363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0] + coef[2, 1:7] = [1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0] + coef[3, 1:7] = [-1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0] + coef[4, 1:7] = [1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0] + coef[5, 1:7] = [-1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0] + coef[6, 1:7] = [1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0] + coef[7, 1:7] = [-1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0] + coef[8, 1:7] = [1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0] + else + error("ENO 系数未实现 order=$spatial_order") + end +end + +# ---------------------- ENO 重构器 ---------------------- +mutable struct EnoReconstructor + spatial_order::Int + ntcells::Int + lmc::Vector{Int} + coef::Matrix{Float64} + dd::Matrix{Float64} + + function EnoReconstructor(spatial_order::Int, ntcells::Int) + lmc = zeros(Int, ntcells) + coef = zeros(Float64, spatial_order + 1, spatial_order) + dd = zeros(Float64, spatial_order, ntcells) + _init_eno_coef!(spatial_order, coef) + new(spatial_order, ntcells, lmc, coef, dd) + end +end + +function reconstruct(rec::EnoReconstructor, q::Vector{Float64}, cfd::Any) + # 1. 差商计算 (dd[1,:] = q) + @views rec.dd[1, :] .= q + for m in 2:rec.spatial_order + for j in 1:(rec.ntcells - m + 1) + rec.dd[m, j] = rec.dd[m-1, j+1] - rec.dd[m-1, j] + end + end + + # 2. 选择 smoothest stencil + domain = cfd.domain + for i in (domain.ist - 1):(domain.ied) # Python: range(ist-1, ied+1) → ied+1-1 = ied + rec.lmc[i] = i + for m in 2:rec.spatial_order + if abs(rec.dd[m, rec.lmc[i] - 1]) < abs(rec.dd[m, rec.lmc[i]]) + rec.lmc[i] -= 1 + end + end + end + + # 3. 重构界面值 + solution = cfd.solution + for i in domain.ist:(domain.ied) # Python: range(ist, ied+1) → ied+1-1 = ied + j = i - domain.ist + 1 # Julia 1-based + k1 = rec.lmc[i - 1] + k2 = rec.lmc[i] + r1 = (i - 1) - k1 + 1 + r2 = i - k2 + 1 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in 1:rec.spatial_order + solution.q_face_left[j] += q[k1 + m - 1] * rec.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m - 1] * rec.coef[r2, m] + end + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/julia/reconstructor/weno3.jl b/example/1d-linear-convection/weno3/julia/02/julia/reconstructor/weno3.jl new file mode 100644 index 00000000..2b6fe1ab --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/julia/reconstructor/weno3.jl @@ -0,0 +1,65 @@ +# julia/reconstructor/weno3.jl +""" +WENO3 重构器(与 reconstructor/weno3.py 完全同构) +""" + +mutable struct Weno3Reconstructor + # 无字段,与 Python 一致 +end + +function reconstruct(rec::Weno3Reconstructor, q::Vector{Float64}, cfd::Any) + domain = cfd.domain + solution = cfd.solution + _reconstruct_left_interfaces(domain, q, solution.q_face_left) + _reconstruct_right_interfaces(domain, q, solution.q_face_right) +end + +function _reconstruct_left_interfaces(domain, u, qL) + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in (domain.ist - 1):(domain.ied - 1) + j = i - (domain.ist - 1) + 1 # ← Julia 1-based: j = i - (ist-1) 对应 Python j = i - (ist-1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = _reconstruct_from_left_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_right_interfaces(domain, u, qR) + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in domain.ist:domain.ied + j = i - domain.ist + 1 # ← Julia 1-based: j = i - ist 对应 Python j = i - ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = _reconstruct_from_right_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_from_left_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -0.5*v1 + 1.5*v2 # r=1 stencil + q1 = 0.5*v2 + 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end + +function _reconstruct_from_right_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 0.5*v1 + 0.5*v2 # r=1 stencil + q1 = 1.5*v2 - 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/julia/residual.jl b/example/1d-linear-convection/weno3/julia/02/julia/residual.jl new file mode 100644 index 00000000..e8fd6584 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/julia/residual.jl @@ -0,0 +1,65 @@ +# julia/residual.jl +""" +残差计算器(与 residual.py 完全同构) +- 封装重建→通量→散度完整流程 +- 依赖 cfd 的多个字段 +""" + +include("mesh.jl") + +mutable struct ResidualCalculator + cfd::Any + config::Any + domain::Any + solution::Any + mesh::Mesh + reconstructor::Any + flux_calculator::Any # 通量计算器(外部传入,替代工厂) + + function ResidualCalculator(cfd::Any, flux_calculator::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + mesh = domain.mesh + reconstructor = cfd.reconstructor + + new(cfd, config, domain, solution, mesh, reconstructor, flux_calculator) + end +end + +""" +计算完整残差(对外唯一接口) +""" +function compute!(calc::ResidualCalculator) + _reconstruct(calc) + _compute_inviscid_flux(calc) + _compute_flux_divergence(calc) +end + +""" +私有方法:界面值重建 +""" +function _reconstruct(calc::ResidualCalculator) + reconstruct(calc.reconstructor, calc.solution.u, calc.cfd) +end + +""" +私有方法:计算无粘通量 +""" +function _compute_inviscid_flux(calc::ResidualCalculator) + compute!(calc.flux_calculator, + calc.solution.q_face_left, + calc.solution.q_face_right, + calc.solution.flux) +end + +""" +私有方法:计算通量散度(残差 = -dF/dx) +""" +function _compute_flux_divergence(calc::ResidualCalculator) + solution = calc.solution + mesh = calc.mesh + for i in 1:mesh.ncells + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / mesh.dx + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/julia/run_eno_weno.jl b/example/1d-linear-convection/weno3/julia/02/julia/run_eno_weno.jl new file mode 100644 index 00000000..58b6cfa5 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/julia/run_eno_weno.jl @@ -0,0 +1,50 @@ +# julia/run_eno_weno.jl +""" +1:1 复刻 run_eno_weno.py 的 Julia 版本 +""" + +include("config.jl") +include("mesh.jl") +include("solver.jl") +include("plotter.jl") + +function performEnoWenoAnalysis() + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + println("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + with_reconstruction(config_eno3, "eno", 3) # 显式指定 3 阶 + config_eno3.dt = 0.0025 # 覆盖默认值 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + run!(cfd_eno3) # 求解并生成 result 字典 + + # 3. 配置并运行 WENO3 求解 + println("Running WENO3 solver...") + config_weno3 = CfdConfig() + with_reconstruction(config_weno3, "weno", 3) # 显式指定 3 阶(WENO 默认 5 阶) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + run!(cfd_weno3) + + # 5. 绘制 ENO/WENO 对比图 + println("Plotting comparison results...") + plot_eno_weno_comparison( + cfd_eno3.result, + cfd_weno3.result; + save_path="eno_weno_comparison.png" + ) + + return cfd_eno3, cfd_weno3 +end + +# 主程序入口 +if abspath(PROGRAM_FILE) == @__FILE__ + performEnoWenoAnalysis() + println("Analysis completed!") +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/julia/solution.jl b/example/1d-linear-convection/weno3/julia/02/julia/solution.jl new file mode 100644 index 00000000..90ca0393 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/julia/solution.jl @@ -0,0 +1,75 @@ +# julia/solution.jl +""" +Solution 结构体(与真实 solution.py 完全同构) +字段顺序、方法、逻辑 1:1 对齐 +""" + +include("mesh.jl") +include("domain.jl") +include("initial_condition.jl") + +mutable struct Solution + domain::Domain + q_face_left::Vector{Float64} + q_face_right::Vector{Float64} + flux::Vector{Float64} + res::Vector{Float64} + u::Vector{Float64} + un::Vector{Float64} + + function Solution(config::Any, domain::Domain) + mesh = domain.mesh + + q_face_left = zeros(Float64, mesh.nnodes) + q_face_right = zeros(Float64, mesh.nnodes) + flux = zeros(Float64, mesh.nnodes) + res = zeros(Float64, mesh.ncells) + u = zeros(Float64, domain.ntcells) + un = zeros(Float64, domain.ntcells) + + sol = new(domain, q_face_left, q_face_right, flux, res, u, un) + initialize_from_config(sol, config) + return sol + end +end + +""" +重置解数组为初始状态 +""" +function reset_solution(sol::Solution) + fill!(sol.u, 0.0) + fill!(sol.un, 0.0) +end + +""" +根据配置初始化场 +注:暂用硬编码替代 InitialConditionFactory +""" +function initialize_from_config(sol::Solution, config::Any) + ic_type = getfield_safe(config, :ic_type, "step") + + # 硬编码创建 IC(替代 InitialConditionFactory) + ic = if ic_type == "step" + StepFunctionIC(config) + elseif ic_type == "sin" + SineWaveIC(config) + elseif ic_type == "gaussian" + GaussianPulseIC(config) + else + error("未知初始条件类型: $ic_type") + end + + apply(ic, sol) # 调用 IC.apply +end + +""" +更新旧场:un = u +""" +function update_old_field(sol::Solution) + sol.un .= sol.u # 等价于 Python 的 un[:] = u[:] +end + +# ---------------------- 辅助函数 ---------------------- +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/julia/solver.jl b/example/1d-linear-convection/weno3/julia/02/julia/solver.jl new file mode 100644 index 00000000..243ccf8f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/julia/solver.jl @@ -0,0 +1,168 @@ +# julia/solver.jl +""" +CFD 求解器主类(与 solver.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("initial_condition.jl") +include("boundary.jl") +include("flux.jl") +include("residual.jl") +include("time_integration.jl") +include("reconstructor/eno.jl") +include("reconstructor/weno3.jl") + +# ---------------------- Cfd 求解器 ---------------------- +mutable struct Cfd + config::Any + domain::Domain + solution::Solution + reconstructor::Any + residual_calculator::ResidualCalculator + integrator::Any + boundary_condition::Any + result::Dict{String, Any} + + function Cfd(config::Any, mesh::Mesh) + domain = Domain(config, mesh) + solution = Solution(config, domain) + + # 在 Cfd 构造函数中 + # =============== 重建器创建 =============== + recon_scheme = config.recon_scheme + spatial_order = config.spatial_order + + # ✅ 模拟 Python ReconstructorFactory 的逻辑 + if recon_scheme == "weno" + recon_scheme = "weno$(spatial_order)" + end + + # 现在根据 recon_scheme 创建 + reconstructor = if recon_scheme == "eno" + EnoReconstructor(spatial_order, domain.ntcells) + elseif recon_scheme == "weno3" + Weno3Reconstructor() + else + error("不支持的重建格式: $recon_scheme") + end + + # 通量计算器 + flux_calculator = if config.flux_type == "rusanov" + RusanovFluxCalculator((config=config, domain=domain)) + elseif config.flux_type == "engquist-osher" + EngquistOsherFluxCalculator((config=config, domain=domain)) + else + error("不支持的通量类型: $(config.flux_type)") + end + + # 残差计算器 + residual_calculator = ResidualCalculator( + (config=config, domain=domain, solution=solution, reconstructor=reconstructor), + flux_calculator + ) + + # 边界条件 + boundary_condition = if config.boundary_type == "periodic" + PeriodicBoundary((config=config, domain=domain)) + elseif config.boundary_type == "dirichlet" + DirichletBoundary((config=config, domain=domain)) + elseif config.boundary_type == "neumann" + NeumannBoundary((config=config, domain=domain)) + else + error("不支持的边界类型: $(config.boundary_type)") + end + + # 构造用于积分器初始化的上下文对象(NamedTuple,模拟 cfd 接口) + integrator_context = ( + config = config, + domain = domain, + solution = solution, + residual_calculator = residual_calculator, + boundary_condition = boundary_condition + ) + + # 使用注册表工厂创建时间推进器 + integrator = create_integrator(integrator_context) + + #@show typeof(integrator) + + # 注入 cfd 到 residual_calculator 和 integrator + residual_calculator.cfd = (config=config, domain=domain, solution=solution, reconstructor=reconstructor, residual_calculator=residual_calculator, integrator=integrator, boundary_condition=boundary_condition) + integrator.base.cfd = residual_calculator.cfd + + result = Dict{String, Any}() + new(config, domain, solution, reconstructor, residual_calculator, integrator, boundary_condition, result) + end +end + +""" +通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界 +""" +function exact_solution(cfd::Cfd) + x = cfd.domain.mesh.xcc + T = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = @. (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = if cfd.config.ic_type == "step" + StepFunctionIC(cfd.config) + elseif cfd.config.ic_type == "sin" + SineWaveIC(cfd.config) + elseif cfd.config.ic_type == "gaussian" + GaussianPulseIC(cfd.config) + else + error("未知初始条件: $(cfd.config.ic_type)") + end + + return evaluate_at(ic, x_shifted) +end + +""" +主求解循环 +""" +function run!(cfd::Cfd) + # 应用初始边界条件并同步 old field + apply!(cfd.boundary_condition, cfd.solution.u) + update_old_field(cfd.solution) + + t = 0.0 + dt_old = cfd.config.dt + dt = dt_old + + while t < cfd.config.final_time + if t + dt > cfd.config.final_time + dt = cfd.config.final_time - t + end + #@show t, dt, maximum(cfd.solution.u), minimum(cfd.solution.u) + # 执行时间步 + step(cfd.integrator, dt) + t += dt + end + + # 恢复 dt + cfd.config.dt = dt_old + + # 整理结果 + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied-1] # Python: [ist:ied] + analytical = exact_solution(cfd) + + cfd.result = Dict( + "x" => cfd.domain.mesh.xcc, + "numerical" => u_numerical, + "analytical" => analytical, + "config" => Dict( + "scheme" => cfd.config.recon_scheme, + "order" => cfd.config.spatial_order, + "rk_order" => cfd.config.rk_order, + "final_time" => cfd.config.final_time + ) + ) + + return u_numerical +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/julia/time_integration.jl b/example/1d-linear-convection/weno3/julia/02/julia/time_integration.jl new file mode 100644 index 00000000..43b540af --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/julia/time_integration.jl @@ -0,0 +1,168 @@ +# julia/time_integration.jl +""" +时间推进器模块(与 time_integration.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("residual.jl") +include("boundary.jl") + +# ---------------------- 抽象时间推进器基类 ---------------------- +abstract type TimeIntegrator end + +mutable struct TimeIntegratorBase <: TimeIntegrator + cfd::Any + config::Any + domain::Domain + solution::Solution + residual_calculator::Any # ResidualCalculator +end + +function TimeIntegratorBase(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + residual_calculator = cfd.residual_calculator + TimeIntegratorBase(cfd, config, domain, solution, residual_calculator) +end + +function compute_residual(integrator::TimeIntegratorBase) + compute!(integrator.residual_calculator) +end + +function apply_boundary(integrator::TimeIntegratorBase) + apply!(integrator.cfd.boundary_condition, integrator.solution.u) +end + +function map_idx(integrator::TimeIntegratorBase, i::Int) + return i - integrator.domain.ist + 1 # ← +1 转为 1-based +end + +# ---------------------- RK1Integrator ---------------------- +mutable struct RK1Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK1Integrator(cfd::Any) + RK1Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK1Integrator, dt::Float64) + compute_residual(integrator.base) + for i in integrator.base.domain.ist:(integrator.base.domain.ied - 1) + j = map_idx(integrator.base, i) + integrator.base.solution.u[i] += dt * integrator.base.solution.res[j] + end + apply_boundary(integrator.base) + update_old_field(integrator.base.solution) +end + +# ---------------------- RK2Integrator ---------------------- +mutable struct RK2Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK2Integrator(cfd::Any) + RK2Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK2Integrator, dt::Float64) + base = integrator.base + # 阶段1:预测步 + compute_residual(base) + u_pred = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u_pred[i] += dt * base.solution.res[j] + end + base.solution.u .= u_pred + apply_boundary(base) + # 阶段2:校正步 + compute_residual(base) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = 0.5 * base.solution.un[i] + 0.5 * base.solution.u[i] + 0.5 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- RK3Integrator ---------------------- +mutable struct RK3Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK3Integrator(cfd::Any) + RK3Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK3Integrator, dt::Float64) + base = integrator.base + # 阶段1 + compute_residual(base) + u1 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u1[i] += dt * base.solution.res[j] + end + base.solution.u .= u1 + apply_boundary(base) + # 阶段2 + compute_residual(base) + u2 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u2[i] = 0.75 * base.solution.un[i] + 0.25 * base.solution.u[i] + 0.25 * dt * base.solution.res[j] + end + base.solution.u .= u2 + apply_boundary(base) + # 阶段3 + compute_residual(base) + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = c1 * base.solution.un[i] + c2 * base.solution.u[i] + c3 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- 注册表 + 工厂(方案2)---------------------- + +const INTEGRATOR_REGISTRY = Dict{String, Function}() + +function register_integrator(name::String, ctor::Function) + if haskey(INTEGRATOR_REGISTRY, name) + @warn "积分器 '$name' 已注册,将被覆盖" + end + INTEGRATOR_REGISTRY[name] = ctor +end + +function create_integrator(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + config = cfd.config + if !hasproperty(config, :rk_order) + error("cfd.config 缺少 rk_order 字段") + end + rk_order = config.rk_order + if !(rk_order isa Integer) || rk_order < 1 + error("rk_order 必须为正整数,当前值: $rk_order (类型: $(typeof(rk_order)))") + end + + name = "rk$rk_order" + if !haskey(INTEGRATOR_REGISTRY, name) + available = sort(collect(keys(INTEGRATOR_REGISTRY))) + error("未注册的时间积分器: '$name'。可用选项: $available") + end + + return INTEGRATOR_REGISTRY[name](cfd) +end + +# 注册内置积分器 +register_integrator("rk1", cfd -> RK1Integrator(cfd)) +register_integrator("rk2", cfd -> RK2Integrator(cfd)) +register_integrator("rk3", cfd -> RK3Integrator(cfd)) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/python/boundary.py b/example/1d-linear-convection/weno3/julia/02/python/boundary.py new file mode 100644 index 00000000..6054f92d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/python/boundary.py @@ -0,0 +1,103 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/python/cfd_registry.py b/example/1d-linear-convection/weno3/julia/02/python/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/python/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/python/config.py b/example/1d-linear-convection/weno3/julia/02/python/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/python/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/python/domain.py b/example/1d-linear-convection/weno3/julia/02/python/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/python/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/python/factories/base_factory.py b/example/1d-linear-convection/weno3/julia/02/python/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/python/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/python/flux.py b/example/1d-linear-convection/weno3/julia/02/python/flux.py new file mode 100644 index 00000000..5ac73aa8 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/python/flux.py @@ -0,0 +1,74 @@ +# flux.py +""" +通量计算器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册,替代硬编码工厂 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象通量计算基类(统一接口) ---------------------- +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass + +# ---------------------- 2. 具体通量计算子类(使用装饰器注册) ---------------------- + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + +class FluxCalculatorFactory: + """通量计算器工厂""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD对象 + + Returns: + 通量计算器实例 + """ + from factories.base_factory import BaseFactory + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/python/gen_boundary_test_data.py b/example/1d-linear-convection/weno3/julia/02/python/gen_boundary_test_data.py new file mode 100644 index 00000000..c7fc2a9c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/python/gen_boundary_test_data.py @@ -0,0 +1,35 @@ +# python/gen_boundary_test_data.py +import numpy as np +from config import CfdConfig +from mesh import Mesh +from domain import Domain + +# 固定测试配置 +config = CfdConfig() +config.with_boundary("dirichlet", left_value=0.5, right_value=1.5) +config.debug = True + +mesh = Mesh() +domain = Domain(config, mesh) + +# 构造 mock CFD 对象(仅含 config + domain) +class MockCfd: + def __init__(self, config, domain): + self.config = config + self.domain = domain + +# 测试用 u:0,1,2,...,N-1 +u_input = np.arange(domain.ntcells, dtype=np.float64) +np.save("u_input.npy", u_input) + +# 测试每种边界 +from boundary import PeriodicBoundary, DirichletBoundary, NeumannBoundary + +for bc_name, bc_class in [("periodic", PeriodicBoundary), ("dirichlet", DirichletBoundary), ("neumann", NeumannBoundary)]: + u = u_input.copy() + cfd_mock = MockCfd(config, domain) + bc = bc_class(cfd_mock) + bc.apply(u) + np.save(f"u_{bc_name}_py.npy", u) + +print("✅ 测试数据已生成:u_*.npy") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/python/gen_ic_test_data.py b/example/1d-linear-convection/weno3/julia/02/python/gen_ic_test_data.py new file mode 100644 index 00000000..69c2573b --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/python/gen_ic_test_data.py @@ -0,0 +1,27 @@ +# python/gen_ic_test_data.py +import sys, os +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +import numpy as np +from config import CfdConfig +from mesh import Mesh +from domain import Domain +from solution import Solution + +# 固定 mesh +mesh = Mesh() +config = CfdConfig() + +# 测试三种 IC +for ic_type in ["step", "sin", "gaussian"]: + config.ic_type = ic_type + domain = Domain(config, mesh) + sol = Solution(config, domain) + + u_full = sol.u.copy() # 包含 ghost + u_interior = sol.u[domain.ist:domain.ied].copy() + + np.save(f"u_{ic_type}_full_py.npy", u_full) + np.save(f"u_{ic_type}_interior_py.npy", u_interior) + +print("✅ 初始条件测试数据已生成") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/python/initial_condition.py b/example/1d-linear-convection/weno3/julia/02/python/initial_condition.py new file mode 100644 index 00000000..047415b7 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/python/initial_condition.py @@ -0,0 +1,90 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, ic_type: str, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', ic_type, config) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/python/mesh.py b/example/1d-linear-convection/weno3/julia/02/python/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/python/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/python/plotter.py b/example/1d-linear-convection/weno3/julia/02/python/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/python/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/python/reconstructor/__init__.py b/example/1d-linear-convection/weno3/julia/02/python/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/python/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/python/reconstructor/base.py b/example/1d-linear-convection/weno3/julia/02/python/reconstructor/base.py new file mode 100644 index 00000000..bbd63850 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/python/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def reconstruct(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/python/reconstructor/eno.py b/example/1d-linear-convection/weno3/julia/02/python/reconstructor/eno.py new file mode 100644 index 00000000..c2fb385d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/python/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def reconstruct(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/python/reconstructor/factory.py b/example/1d-linear-convection/weno3/julia/02/python/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/python/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/python/reconstructor/weno3.py b/example/1d-linear-convection/weno3/julia/02/python/reconstructor/weno3.py new file mode 100644 index 00000000..bf68be50 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/python/reconstructor/weno3.py @@ -0,0 +1,59 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def reconstruct(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/julia/02/python/registry.py b/example/1d-linear-convection/weno3/julia/02/python/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/python/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/python/residual.py b/example/1d-linear-convection/weno3/julia/02/python/residual.py new file mode 100644 index 00000000..b4d4d7dc --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/python/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._reconstruct() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _reconstruct(self): + """私有方法:界面值重建""" + self.reconstructor.reconstruct(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/python/run_eno_weno.py b/example/1d-linear-convection/weno3/julia/02/python/run_eno_weno.py new file mode 100644 index 00000000..ff46f226 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/python/run_eno_weno.py @@ -0,0 +1,54 @@ +# run_eno_weno.py + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/python/solution.py b/example/1d-linear-convection/weno3/julia/02/python/solution.py new file mode 100644 index 00000000..92a46d3f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/python/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config.ic_type, config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02/python/solver.py b/example/1d-linear-convection/weno3/julia/02/python/solver.py new file mode 100644 index 00000000..fdb5e46a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/python/solver.py @@ -0,0 +1,85 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + + +from flux import InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory + +from boundary import BoundaryCondition, PeriodicBoundary, DirichletBoundary, NeumannBoundary, BoundaryConditionFactory + +from time_integration import TimeIntegrator, RK1Integrator, RK2Integrator, RK3Integrator, TimeIntegratorFactory + +from mesh import Mesh + +from reconstructor import ReconstructorFactory + +from initial_condition import InitialCondition, StepFunctionIC, SineWaveIC, GaussianPulseIC, InitialConditionFactory + +from domain import Domain +from solution import Solution + +from config import CfdConfig +from residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config.ic_type, self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/julia/02/python/time_integration.py b/example/1d-linear-convection/weno3/julia/02/python/time_integration.py new file mode 100644 index 00000000..25ac0b4c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02/python/time_integration.py @@ -0,0 +1,125 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class TimeIntegratorFactory: + """时间推进器工厂""" + + @staticmethod + def create(cfd) -> 'TimeIntegrator': + """ + 创建时间推进器实例 + + Args: + cfd: CFD对象 + + Returns: + 时间推进器实例 + """ + from factories.base_factory import BaseFactory + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/julia/boundary.jl b/example/1d-linear-convection/weno3/julia/02a/julia/boundary.jl new file mode 100644 index 00000000..5b0baaf4 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/julia/boundary.jl @@ -0,0 +1,90 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/julia/config.jl b/example/1d-linear-convection/weno3/julia/02a/julia/config.jl new file mode 100644 index 00000000..bf48de3a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/julia/config.jl @@ -0,0 +1,68 @@ +# julia/config.jl +""" +CfdConfig:与 Python config.py 完全同构 +""" +mutable struct CfdConfig + ic_type::String + recon_scheme::String + flux_type::String + rk_order::Int + wave_speed::Float64 + final_time::Float64 + dt::Float64 + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + spatial_order::Int + + function CfdConfig() + new( + "step", + "eno", + "rusanov", + 1, + 1.0, + 0.625, + 0.025, + "periodic", + 1.0, + 2.0, + 2 + ) + end +end + +""" +专用配置:重建方案(链式调用) +""" +function with_reconstruction(cfg::CfdConfig, scheme::String, order::Union{Int, Nothing}=nothing) + cfg.recon_scheme = lowercase(scheme) + + if order !== nothing + cfg.spatial_order = order + else + if startswith(cfg.recon_scheme, "weno") + cfg.spatial_order = 5 + elseif cfg.recon_scheme == "eno" + cfg.spatial_order = 3 + else + error("不支持的重建格式:$scheme(仅支持 eno/weno)") + end + end + + return cfg # 支持链式调用 +end + +""" +专用配置:边界条件(链式调用) +""" +function with_boundary(cfg::CfdConfig, bc_type::String; left_value=nothing, right_value=nothing) + cfg.boundary_type = bc_type + if left_value !== nothing + cfg.left_boundary_value = left_value + end + if right_value !== nothing + cfg.right_boundary_value = right_value + end + return cfg +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/julia/domain.jl b/example/1d-linear-convection/weno3/julia/02a/julia/domain.jl new file mode 100644 index 00000000..a7edc226 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/julia/domain.jl @@ -0,0 +1,61 @@ +# julia/domain.jl +""" +Domain 结构体(与 domain.py 完全同构) +- 保存 config +- nghosts 计算逻辑 1:1 +- ist/ied 为 0-based(与 Python 一致) +""" + +include("mesh.jl") + +mutable struct Domain + config::Any # 保存 config(与 Python 一致) + mesh::Mesh + nghosts::Int + ist::Int # 0-based + ied::Int # 0-based + ntcells::Int +end + +function Domain(config::Any, mesh::Mesh) + nghosts = _calc_nghosts(config) + ist = nghosts + 1 # ← 1-based 起始索引 + ied = ist + mesh.ncells # ← 1-based 结束索引(不包含) + ntcells = mesh.ncells + 2 * nghosts + Domain(config, mesh, nghosts, ist, ied, ntcells) +end + +function _calc_nghosts(config::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + order = getfield_safe(config, :spatial_order, 2) + + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + if scheme == "eno" + nghosts = order + elseif startswith(scheme, "weno") + nghosts = order ÷ 2 + 1 + else + error("未知重建格式 $(scheme),无法计算ghost层!") + end + + if nghosts <= 0 + error("计算得到的ghost层数量无效:$(nghosts)(阶数$(order),格式$(scheme))") + end + + return nghosts +end + +function is_physical_cell(domain::Domain, idx::Int) + return domain.ist <= idx < domain.ied +end + +function get_physical_indices(domain::Domain) + return domain.ist:(domain.ied - 1) +end + +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/julia/flux.jl b/example/1d-linear-convection/weno3/julia/02a/julia/flux.jl new file mode 100644 index 00000000..1252a037 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/julia/flux.jl @@ -0,0 +1,116 @@ +# julia/flux.jl +""" +通量计算器模块(与 flux.py 完全同构) +- 抽象基类 + 具体实现 +- 字段:cfd, config, mesh, wave_speed +""" + +include("mesh.jl") + +# ---------------------- 抽象基类 ---------------------- +""" +InviscidFluxCalculator 抽象类型 +Julia 无 ABC,用文档约定 +所有子类型必须实现 compute! +""" +abstract type InviscidFluxCalculator end + +# ---------------------- RusanovFluxCalculator ---------------------- +mutable struct RusanovFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function RusanovFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::RusanovFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = calc.wave_speed + c_R = calc.wave_speed + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + end +end + +# ---------------------- EngquistOsherFluxCalculator ---------------------- +mutable struct EngquistOsherFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function EngquistOsherFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::EngquistOsherFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + c = calc.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + end +end + +# ---------------------- 通量计算器注册表 + 工厂(方案2)---------------------- + +""" +全局通量注册表:flux 名称 → 构造函数 +""" +const FLUX_REGISTRY = Dict{String, Function}() + +""" +注册通量计算器 +""" +function register_flux(name::String, ctor::Function) + if haskey(FLUX_REGISTRY, name) + @warn "通量计算器 '$name' 已注册,将被覆盖" + end + FLUX_REGISTRY[name] = ctor +end + +""" +工厂函数:根据 cfd.config.flux_type 创建通量计算器 +""" +function create_flux_calculator(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + config = cfd.config + if !hasproperty(config, :flux_type) + error("cfd.config 缺少 flux_type 字段") + end + + flux_type = config.flux_type + if !(flux_type isa AbstractString) + error("flux_type 必须为字符串,当前值: $flux_type") + end + + if !haskey(FLUX_REGISTRY, flux_type) + available = sort(collect(keys(FLUX_REGISTRY))) + error("未注册的通量计算器: '$flux_type'。可用选项: $available") + end + + return FLUX_REGISTRY[flux_type](cfd) +end + +# ---------------------- 注册内置通量计算器 ---------------------- +register_flux("rusanov", cfd -> RusanovFluxCalculator(cfd)) +register_flux("engquist-osher", cfd -> EngquistOsherFluxCalculator(cfd)) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/julia/initial_condition.jl b/example/1d-linear-convection/weno3/julia/02a/julia/initial_condition.jl new file mode 100644 index 00000000..5e029e8d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/julia/initial_condition.jl @@ -0,0 +1,86 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/julia/mesh.jl b/example/1d-linear-convection/weno3/julia/02a/julia/mesh.jl new file mode 100644 index 00000000..1b0dd4bf --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/julia/mesh.jl @@ -0,0 +1,45 @@ +# julia/mesh.jl +""" +Mesh 结构体(与提供的 mesh.py 完全同构) +- 字段名、初始化顺序、循环逻辑完全一致 +- 不做任何泛化或优化 +""" + +mutable struct Mesh + xmin::Float64 + xmax::Float64 + ncells::Int + nnodes::Int + nx::Int + x::Vector{Float64} + xcc::Vector{Float64} + L::Float64 # 在 init_mesh 中赋值 + dx::Float64 # 在 init_mesh 中赋值 + + function Mesh() + # 与 Python __init__ 完全一致 + xmin = 0.0 + xmax = 2.0 + ncells = 40 + nnodes = ncells + 1 + nx = ncells + x = zeros(Float64, nnodes) + xcc = zeros(Float64, ncells) + + # 调用 init_mesh(模拟 Python) + L = xmax - xmin + dx = L / ncells + + # Generate node coordinates: for i in range(self.nnodes) + for i in 1:nnodes + x[i] = xmin + (i - 1) * dx # Python i → Julia i-1 + end + + # Generate cell center coordinates + for i in 1:ncells + xcc[i] = 0.5 * (x[i] + x[i+1]) + end + + new(xmin, xmax, ncells, nnodes, nx, x, xcc, L, dx) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/julia/plotter.jl b/example/1d-linear-convection/weno3/julia/02a/julia/plotter.jl new file mode 100644 index 00000000..a77d8d68 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/julia/plotter.jl @@ -0,0 +1,147 @@ +# julia/plotter.jl +""" +CFDPlotter 的 Julia 实现(通过 PythonCall.jl 调用 Matplotlib) +确保与 Python plotter.py 行为完全一致 +""" + +using PythonCall + +# 初始化 Python 环境(加载 matplotlib, inflect) +const plt = pyimport("matplotlib.pyplot") +const inflect = pyimport("inflect") + +mutable struct CFDPlotter + default_styles::Dict{String, Any} + p::Py +end + +function CFDPlotter() + default_styles = Dict{String, Any}( + "numerical" => Dict( + :color => "blue", + :linestyle => "-", + :marker => "o", + :markerfacecolor => "none" + ), + "analytical" => Dict( + :color => "red", + :linestyle => "--", + :marker => "", + :linewidth => 1.5 + ), + "comparison" => [ + Dict(:color => "black", :linestyle => "-", :marker => "o", :markerfacecolor => "none"), + Dict(:color => "blue", :linestyle => "--", :marker => "s", :markerfacecolor => "none"), + Dict(:color => "green", :linestyle => ":", :marker => "^", :markerfacecolor => "none") + ] + ) + p = inflect.engine() + CFDPlotter(default_styles, p) +end + +""" +轻量即时绘图(快速验证结果) +""" +function plot_quick(plotter::CFDPlotter, cfd_result::Dict; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + rk_order = cfd_result["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = cfd_result["config"]["final_time"] + order = cfd_result["config"]["order"] + scheme = uppercase(cfd_result["config"]["scheme"]) + actual_title = "1D Convection (t=$(final_time))\n$(order)th-order $(scheme) + $(rk_str)-order RK" + end + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"]; + label="Numerical ($(uppercase(cfd_result["config"]["scheme"])))", + plotter.default_styles["numerical"]..., + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + # 通用样式 + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +""" +多格式/多精度对比绘图 +""" +function plot_comparison(plotter::CFDPlotter, result_list::Vector{Dict{String, Any}}; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + schemes = [uppercase(r["config"]["scheme"]) * string(r["config"]["order"]) for r in result_list] + rk_order = result_list[1]["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = result_list[1]["config"]["final_time"] + actual_title = "1D Convection Comparison (t=$(final_time))\n$(join(schemes, ", ")) + $(rk_str)-order RK" + end + + # 绘制多个数值解 + for (i, res) in enumerate(result_list) + style = plotter.default_styles["comparison"][mod1(i, length(plotter.default_styles["comparison"]))] + label = "Numerical ($(uppercase(res["config"]["scheme"]))$(res["config"]["order"]))" + plt.plot( + res["x"], res["numerical"]; + label=label, + style..., + markersize=5, linewidth=0.5 + ) + end + + # 绘制解析解 + plt.plot( + result_list[1]["x"], result_list[1]["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +function _set_common_style(plotter::CFDPlotter, title::String) + plt.title(title, fontsize=12) + plt.xlabel("x", fontsize=10) + plt.ylabel("u", fontsize=10) + plt.legend(fontsize=9) + plt.grid(true, color="gray", linestyle="--", linewidth=0.5, alpha=0.7) + plt.tight_layout() +end + +""" +快捷函数:ENO/WENO对比绘图 +""" +function plot_eno_weno_comparison(eno_result::Dict, weno_result::Dict; save_path=nothing) + plotter = CFDPlotter() + plot_comparison(plotter, [eno_result, weno_result]; save_path=save_path) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/julia/reconstructor/eno.jl b/example/1d-linear-convection/weno3/julia/02a/julia/reconstructor/eno.jl new file mode 100644 index 00000000..e78a636f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/julia/reconstructor/eno.jl @@ -0,0 +1,107 @@ +# julia/reconstructor/eno.jl +""" +ENO 重构器(与 reconstructor/eno.py 完全同构) +""" + +# ---------------------- ENO 系数初始化 ---------------------- +function _init_eno_coef!(spatial_order::Int, coef::Matrix{Float64}) + if spatial_order == 1 + coef[1, 1] = 1.0 + coef[2, 1] = 1.0 + elseif spatial_order == 2 + coef[1, 1:2] = [3.0/2.0, -1.0/2.0] + coef[2, 1:2] = [1.0/2.0, 1.0/2.0] + coef[3, 1:2] = [-1.0/2.0, 3.0/2.0] + elseif spatial_order == 3 + coef[1, 1:3] = [11.0/6.0, -7.0/6.0, 1.0/3.0] + coef[2, 1:3] = [1.0/3.0, 5.0/6.0, -1.0/6.0] + coef[3, 1:3] = [-1.0/6.0, 5.0/6.0, 1.0/3.0] + coef[4, 1:3] = [1.0/3.0, -7.0/6.0, 11.0/6.0] + elseif spatial_order == 4 + coef[1, 1:4] = [25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0] + coef[2, 1:4] = [1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0] + coef[3, 1:4] = [-1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0] + coef[4, 1:4] = [1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0] + coef[5, 1:4] = [-1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0] + elseif spatial_order == 5 + coef[1, 1:5] = [137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0] + coef[2, 1:5] = [1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0] + coef[3, 1:5] = [-1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0] + coef[4, 1:5] = [1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0] + coef[5, 1:5] = [-1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0] + coef[6, 1:5] = [1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0] + elseif spatial_order == 6 + coef[1, 1:6] = [49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0] + coef[2, 1:6] = [1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0] + coef[3, 1:6] = [-1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0] + coef[4, 1:6] = [1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0] + coef[5, 1:6] = [-1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0] + coef[6, 1:6] = [1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0] + coef[7, 1:6] = [-1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0] + elseif spatial_order == 7 + coef[1, 1:7] = [363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0] + coef[2, 1:7] = [1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0] + coef[3, 1:7] = [-1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0] + coef[4, 1:7] = [1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0] + coef[5, 1:7] = [-1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0] + coef[6, 1:7] = [1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0] + coef[7, 1:7] = [-1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0] + coef[8, 1:7] = [1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0] + else + error("ENO 系数未实现 order=$spatial_order") + end +end + +# ---------------------- ENO 重构器 ---------------------- +mutable struct EnoReconstructor + spatial_order::Int + ntcells::Int + lmc::Vector{Int} + coef::Matrix{Float64} + dd::Matrix{Float64} + + function EnoReconstructor(spatial_order::Int, ntcells::Int) + lmc = zeros(Int, ntcells) + coef = zeros(Float64, spatial_order + 1, spatial_order) + dd = zeros(Float64, spatial_order, ntcells) + _init_eno_coef!(spatial_order, coef) + new(spatial_order, ntcells, lmc, coef, dd) + end +end + +function reconstruct(rec::EnoReconstructor, q::Vector{Float64}, cfd::Any) + # 1. 差商计算 (dd[1,:] = q) + @views rec.dd[1, :] .= q + for m in 2:rec.spatial_order + for j in 1:(rec.ntcells - m + 1) + rec.dd[m, j] = rec.dd[m-1, j+1] - rec.dd[m-1, j] + end + end + + # 2. 选择 smoothest stencil + domain = cfd.domain + for i in (domain.ist - 1):(domain.ied) # Python: range(ist-1, ied+1) → ied+1-1 = ied + rec.lmc[i] = i + for m in 2:rec.spatial_order + if abs(rec.dd[m, rec.lmc[i] - 1]) < abs(rec.dd[m, rec.lmc[i]]) + rec.lmc[i] -= 1 + end + end + end + + # 3. 重构界面值 + solution = cfd.solution + for i in domain.ist:(domain.ied) # Python: range(ist, ied+1) → ied+1-1 = ied + j = i - domain.ist + 1 # Julia 1-based + k1 = rec.lmc[i - 1] + k2 = rec.lmc[i] + r1 = (i - 1) - k1 + 1 + r2 = i - k2 + 1 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in 1:rec.spatial_order + solution.q_face_left[j] += q[k1 + m - 1] * rec.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m - 1] * rec.coef[r2, m] + end + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/julia/reconstructor/weno3.jl b/example/1d-linear-convection/weno3/julia/02a/julia/reconstructor/weno3.jl new file mode 100644 index 00000000..2b6fe1ab --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/julia/reconstructor/weno3.jl @@ -0,0 +1,65 @@ +# julia/reconstructor/weno3.jl +""" +WENO3 重构器(与 reconstructor/weno3.py 完全同构) +""" + +mutable struct Weno3Reconstructor + # 无字段,与 Python 一致 +end + +function reconstruct(rec::Weno3Reconstructor, q::Vector{Float64}, cfd::Any) + domain = cfd.domain + solution = cfd.solution + _reconstruct_left_interfaces(domain, q, solution.q_face_left) + _reconstruct_right_interfaces(domain, q, solution.q_face_right) +end + +function _reconstruct_left_interfaces(domain, u, qL) + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in (domain.ist - 1):(domain.ied - 1) + j = i - (domain.ist - 1) + 1 # ← Julia 1-based: j = i - (ist-1) 对应 Python j = i - (ist-1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = _reconstruct_from_left_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_right_interfaces(domain, u, qR) + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in domain.ist:domain.ied + j = i - domain.ist + 1 # ← Julia 1-based: j = i - ist 对应 Python j = i - ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = _reconstruct_from_right_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_from_left_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -0.5*v1 + 1.5*v2 # r=1 stencil + q1 = 0.5*v2 + 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end + +function _reconstruct_from_right_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 0.5*v1 + 0.5*v2 # r=1 stencil + q1 = 1.5*v2 - 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/julia/residual.jl b/example/1d-linear-convection/weno3/julia/02a/julia/residual.jl new file mode 100644 index 00000000..e8fd6584 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/julia/residual.jl @@ -0,0 +1,65 @@ +# julia/residual.jl +""" +残差计算器(与 residual.py 完全同构) +- 封装重建→通量→散度完整流程 +- 依赖 cfd 的多个字段 +""" + +include("mesh.jl") + +mutable struct ResidualCalculator + cfd::Any + config::Any + domain::Any + solution::Any + mesh::Mesh + reconstructor::Any + flux_calculator::Any # 通量计算器(外部传入,替代工厂) + + function ResidualCalculator(cfd::Any, flux_calculator::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + mesh = domain.mesh + reconstructor = cfd.reconstructor + + new(cfd, config, domain, solution, mesh, reconstructor, flux_calculator) + end +end + +""" +计算完整残差(对外唯一接口) +""" +function compute!(calc::ResidualCalculator) + _reconstruct(calc) + _compute_inviscid_flux(calc) + _compute_flux_divergence(calc) +end + +""" +私有方法:界面值重建 +""" +function _reconstruct(calc::ResidualCalculator) + reconstruct(calc.reconstructor, calc.solution.u, calc.cfd) +end + +""" +私有方法:计算无粘通量 +""" +function _compute_inviscid_flux(calc::ResidualCalculator) + compute!(calc.flux_calculator, + calc.solution.q_face_left, + calc.solution.q_face_right, + calc.solution.flux) +end + +""" +私有方法:计算通量散度(残差 = -dF/dx) +""" +function _compute_flux_divergence(calc::ResidualCalculator) + solution = calc.solution + mesh = calc.mesh + for i in 1:mesh.ncells + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / mesh.dx + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/julia/run_eno_weno.jl b/example/1d-linear-convection/weno3/julia/02a/julia/run_eno_weno.jl new file mode 100644 index 00000000..58b6cfa5 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/julia/run_eno_weno.jl @@ -0,0 +1,50 @@ +# julia/run_eno_weno.jl +""" +1:1 复刻 run_eno_weno.py 的 Julia 版本 +""" + +include("config.jl") +include("mesh.jl") +include("solver.jl") +include("plotter.jl") + +function performEnoWenoAnalysis() + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + println("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + with_reconstruction(config_eno3, "eno", 3) # 显式指定 3 阶 + config_eno3.dt = 0.0025 # 覆盖默认值 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + run!(cfd_eno3) # 求解并生成 result 字典 + + # 3. 配置并运行 WENO3 求解 + println("Running WENO3 solver...") + config_weno3 = CfdConfig() + with_reconstruction(config_weno3, "weno", 3) # 显式指定 3 阶(WENO 默认 5 阶) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + run!(cfd_weno3) + + # 5. 绘制 ENO/WENO 对比图 + println("Plotting comparison results...") + plot_eno_weno_comparison( + cfd_eno3.result, + cfd_weno3.result; + save_path="eno_weno_comparison.png" + ) + + return cfd_eno3, cfd_weno3 +end + +# 主程序入口 +if abspath(PROGRAM_FILE) == @__FILE__ + performEnoWenoAnalysis() + println("Analysis completed!") +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/julia/solution.jl b/example/1d-linear-convection/weno3/julia/02a/julia/solution.jl new file mode 100644 index 00000000..90ca0393 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/julia/solution.jl @@ -0,0 +1,75 @@ +# julia/solution.jl +""" +Solution 结构体(与真实 solution.py 完全同构) +字段顺序、方法、逻辑 1:1 对齐 +""" + +include("mesh.jl") +include("domain.jl") +include("initial_condition.jl") + +mutable struct Solution + domain::Domain + q_face_left::Vector{Float64} + q_face_right::Vector{Float64} + flux::Vector{Float64} + res::Vector{Float64} + u::Vector{Float64} + un::Vector{Float64} + + function Solution(config::Any, domain::Domain) + mesh = domain.mesh + + q_face_left = zeros(Float64, mesh.nnodes) + q_face_right = zeros(Float64, mesh.nnodes) + flux = zeros(Float64, mesh.nnodes) + res = zeros(Float64, mesh.ncells) + u = zeros(Float64, domain.ntcells) + un = zeros(Float64, domain.ntcells) + + sol = new(domain, q_face_left, q_face_right, flux, res, u, un) + initialize_from_config(sol, config) + return sol + end +end + +""" +重置解数组为初始状态 +""" +function reset_solution(sol::Solution) + fill!(sol.u, 0.0) + fill!(sol.un, 0.0) +end + +""" +根据配置初始化场 +注:暂用硬编码替代 InitialConditionFactory +""" +function initialize_from_config(sol::Solution, config::Any) + ic_type = getfield_safe(config, :ic_type, "step") + + # 硬编码创建 IC(替代 InitialConditionFactory) + ic = if ic_type == "step" + StepFunctionIC(config) + elseif ic_type == "sin" + SineWaveIC(config) + elseif ic_type == "gaussian" + GaussianPulseIC(config) + else + error("未知初始条件类型: $ic_type") + end + + apply(ic, sol) # 调用 IC.apply +end + +""" +更新旧场:un = u +""" +function update_old_field(sol::Solution) + sol.un .= sol.u # 等价于 Python 的 un[:] = u[:] +end + +# ---------------------- 辅助函数 ---------------------- +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/julia/solver.jl b/example/1d-linear-convection/weno3/julia/02a/julia/solver.jl new file mode 100644 index 00000000..d09d5c5f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/julia/solver.jl @@ -0,0 +1,169 @@ +# julia/solver.jl +""" +CFD 求解器主类(与 solver.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("initial_condition.jl") +include("boundary.jl") +include("flux.jl") +include("residual.jl") +include("time_integration.jl") +include("reconstructor/eno.jl") +include("reconstructor/weno3.jl") + +# ---------------------- Cfd 求解器 ---------------------- +mutable struct Cfd + config::Any + domain::Domain + solution::Solution + reconstructor::Any + residual_calculator::ResidualCalculator + integrator::Any + boundary_condition::Any + result::Dict{String, Any} + + function Cfd(config::Any, mesh::Mesh) + domain = Domain(config, mesh) + solution = Solution(config, domain) + + # 在 Cfd 构造函数中 + # =============== 重建器创建 =============== + recon_scheme = config.recon_scheme + spatial_order = config.spatial_order + + # ✅ 模拟 Python ReconstructorFactory 的逻辑 + if recon_scheme == "weno" + recon_scheme = "weno$(spatial_order)" + end + + # 现在根据 recon_scheme 创建 + reconstructor = if recon_scheme == "eno" + EnoReconstructor(spatial_order, domain.ntcells) + elseif recon_scheme == "weno3" + Weno3Reconstructor() + else + error("不支持的重建格式: $recon_scheme") + end + + # 构造用于通量初始化的上下文对象(NamedTuple) + flux_context = ( + config = config, + domain = domain + ) + + # 使用工厂创建通量计算器 + flux_calculator = create_flux_calculator(flux_context) + + # 残差计算器 + residual_calculator = ResidualCalculator( + (config=config, domain=domain, solution=solution, reconstructor=reconstructor), + flux_calculator + ) + + # 边界条件 + boundary_condition = if config.boundary_type == "periodic" + PeriodicBoundary((config=config, domain=domain)) + elseif config.boundary_type == "dirichlet" + DirichletBoundary((config=config, domain=domain)) + elseif config.boundary_type == "neumann" + NeumannBoundary((config=config, domain=domain)) + else + error("不支持的边界类型: $(config.boundary_type)") + end + + + # 构造用于积分器初始化的上下文对象(NamedTuple,模拟 cfd 接口) + integrator_context = ( + config = config, + domain = domain, + solution = solution, + residual_calculator = residual_calculator, + boundary_condition = boundary_condition + ) + + # 使用注册表工厂创建时间推进器 + integrator = create_integrator(integrator_context) + + #@show typeof(integrator) + + # 注入 cfd 到 residual_calculator 和 integrator + residual_calculator.cfd = (config=config, domain=domain, solution=solution, reconstructor=reconstructor, residual_calculator=residual_calculator, integrator=integrator, boundary_condition=boundary_condition) + integrator.base.cfd = residual_calculator.cfd + + result = Dict{String, Any}() + new(config, domain, solution, reconstructor, residual_calculator, integrator, boundary_condition, result) + end +end + +""" +通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界 +""" +function exact_solution(cfd::Cfd) + x = cfd.domain.mesh.xcc + T = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = @. (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = if cfd.config.ic_type == "step" + StepFunctionIC(cfd.config) + elseif cfd.config.ic_type == "sin" + SineWaveIC(cfd.config) + elseif cfd.config.ic_type == "gaussian" + GaussianPulseIC(cfd.config) + else + error("未知初始条件: $(cfd.config.ic_type)") + end + + return evaluate_at(ic, x_shifted) +end + +""" +主求解循环 +""" +function run!(cfd::Cfd) + # 应用初始边界条件并同步 old field + apply!(cfd.boundary_condition, cfd.solution.u) + update_old_field(cfd.solution) + + t = 0.0 + dt_old = cfd.config.dt + dt = dt_old + + while t < cfd.config.final_time + if t + dt > cfd.config.final_time + dt = cfd.config.final_time - t + end + #@show t, dt, maximum(cfd.solution.u), minimum(cfd.solution.u) + # 执行时间步 + step(cfd.integrator, dt) + t += dt + end + + # 恢复 dt + cfd.config.dt = dt_old + + # 整理结果 + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied-1] # Python: [ist:ied] + analytical = exact_solution(cfd) + + cfd.result = Dict( + "x" => cfd.domain.mesh.xcc, + "numerical" => u_numerical, + "analytical" => analytical, + "config" => Dict( + "scheme" => cfd.config.recon_scheme, + "order" => cfd.config.spatial_order, + "rk_order" => cfd.config.rk_order, + "final_time" => cfd.config.final_time + ) + ) + + return u_numerical +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/julia/time_integration.jl b/example/1d-linear-convection/weno3/julia/02a/julia/time_integration.jl new file mode 100644 index 00000000..43b540af --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/julia/time_integration.jl @@ -0,0 +1,168 @@ +# julia/time_integration.jl +""" +时间推进器模块(与 time_integration.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("residual.jl") +include("boundary.jl") + +# ---------------------- 抽象时间推进器基类 ---------------------- +abstract type TimeIntegrator end + +mutable struct TimeIntegratorBase <: TimeIntegrator + cfd::Any + config::Any + domain::Domain + solution::Solution + residual_calculator::Any # ResidualCalculator +end + +function TimeIntegratorBase(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + residual_calculator = cfd.residual_calculator + TimeIntegratorBase(cfd, config, domain, solution, residual_calculator) +end + +function compute_residual(integrator::TimeIntegratorBase) + compute!(integrator.residual_calculator) +end + +function apply_boundary(integrator::TimeIntegratorBase) + apply!(integrator.cfd.boundary_condition, integrator.solution.u) +end + +function map_idx(integrator::TimeIntegratorBase, i::Int) + return i - integrator.domain.ist + 1 # ← +1 转为 1-based +end + +# ---------------------- RK1Integrator ---------------------- +mutable struct RK1Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK1Integrator(cfd::Any) + RK1Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK1Integrator, dt::Float64) + compute_residual(integrator.base) + for i in integrator.base.domain.ist:(integrator.base.domain.ied - 1) + j = map_idx(integrator.base, i) + integrator.base.solution.u[i] += dt * integrator.base.solution.res[j] + end + apply_boundary(integrator.base) + update_old_field(integrator.base.solution) +end + +# ---------------------- RK2Integrator ---------------------- +mutable struct RK2Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK2Integrator(cfd::Any) + RK2Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK2Integrator, dt::Float64) + base = integrator.base + # 阶段1:预测步 + compute_residual(base) + u_pred = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u_pred[i] += dt * base.solution.res[j] + end + base.solution.u .= u_pred + apply_boundary(base) + # 阶段2:校正步 + compute_residual(base) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = 0.5 * base.solution.un[i] + 0.5 * base.solution.u[i] + 0.5 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- RK3Integrator ---------------------- +mutable struct RK3Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK3Integrator(cfd::Any) + RK3Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK3Integrator, dt::Float64) + base = integrator.base + # 阶段1 + compute_residual(base) + u1 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u1[i] += dt * base.solution.res[j] + end + base.solution.u .= u1 + apply_boundary(base) + # 阶段2 + compute_residual(base) + u2 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u2[i] = 0.75 * base.solution.un[i] + 0.25 * base.solution.u[i] + 0.25 * dt * base.solution.res[j] + end + base.solution.u .= u2 + apply_boundary(base) + # 阶段3 + compute_residual(base) + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = c1 * base.solution.un[i] + c2 * base.solution.u[i] + c3 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- 注册表 + 工厂(方案2)---------------------- + +const INTEGRATOR_REGISTRY = Dict{String, Function}() + +function register_integrator(name::String, ctor::Function) + if haskey(INTEGRATOR_REGISTRY, name) + @warn "积分器 '$name' 已注册,将被覆盖" + end + INTEGRATOR_REGISTRY[name] = ctor +end + +function create_integrator(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + config = cfd.config + if !hasproperty(config, :rk_order) + error("cfd.config 缺少 rk_order 字段") + end + rk_order = config.rk_order + if !(rk_order isa Integer) || rk_order < 1 + error("rk_order 必须为正整数,当前值: $rk_order (类型: $(typeof(rk_order)))") + end + + name = "rk$rk_order" + if !haskey(INTEGRATOR_REGISTRY, name) + available = sort(collect(keys(INTEGRATOR_REGISTRY))) + error("未注册的时间积分器: '$name'。可用选项: $available") + end + + return INTEGRATOR_REGISTRY[name](cfd) +end + +# 注册内置积分器 +register_integrator("rk1", cfd -> RK1Integrator(cfd)) +register_integrator("rk2", cfd -> RK2Integrator(cfd)) +register_integrator("rk3", cfd -> RK3Integrator(cfd)) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/python/boundary.py b/example/1d-linear-convection/weno3/julia/02a/python/boundary.py new file mode 100644 index 00000000..6054f92d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/python/boundary.py @@ -0,0 +1,103 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/python/cfd_registry.py b/example/1d-linear-convection/weno3/julia/02a/python/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/python/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/python/config.py b/example/1d-linear-convection/weno3/julia/02a/python/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/python/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/python/domain.py b/example/1d-linear-convection/weno3/julia/02a/python/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/python/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/python/factories/base_factory.py b/example/1d-linear-convection/weno3/julia/02a/python/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/python/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/python/flux.py b/example/1d-linear-convection/weno3/julia/02a/python/flux.py new file mode 100644 index 00000000..5ac73aa8 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/python/flux.py @@ -0,0 +1,74 @@ +# flux.py +""" +通量计算器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册,替代硬编码工厂 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象通量计算基类(统一接口) ---------------------- +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass + +# ---------------------- 2. 具体通量计算子类(使用装饰器注册) ---------------------- + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + +class FluxCalculatorFactory: + """通量计算器工厂""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD对象 + + Returns: + 通量计算器实例 + """ + from factories.base_factory import BaseFactory + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/python/initial_condition.py b/example/1d-linear-convection/weno3/julia/02a/python/initial_condition.py new file mode 100644 index 00000000..047415b7 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/python/initial_condition.py @@ -0,0 +1,90 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, ic_type: str, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', ic_type, config) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/python/mesh.py b/example/1d-linear-convection/weno3/julia/02a/python/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/python/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/python/plotter.py b/example/1d-linear-convection/weno3/julia/02a/python/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/python/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/python/reconstructor/__init__.py b/example/1d-linear-convection/weno3/julia/02a/python/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/python/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/python/reconstructor/base.py b/example/1d-linear-convection/weno3/julia/02a/python/reconstructor/base.py new file mode 100644 index 00000000..bbd63850 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/python/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def reconstruct(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/python/reconstructor/eno.py b/example/1d-linear-convection/weno3/julia/02a/python/reconstructor/eno.py new file mode 100644 index 00000000..c2fb385d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/python/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def reconstruct(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/python/reconstructor/factory.py b/example/1d-linear-convection/weno3/julia/02a/python/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/python/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/python/reconstructor/weno3.py b/example/1d-linear-convection/weno3/julia/02a/python/reconstructor/weno3.py new file mode 100644 index 00000000..bf68be50 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/python/reconstructor/weno3.py @@ -0,0 +1,59 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def reconstruct(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/julia/02a/python/registry.py b/example/1d-linear-convection/weno3/julia/02a/python/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/python/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/python/residual.py b/example/1d-linear-convection/weno3/julia/02a/python/residual.py new file mode 100644 index 00000000..b4d4d7dc --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/python/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._reconstruct() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _reconstruct(self): + """私有方法:界面值重建""" + self.reconstructor.reconstruct(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/python/run_eno_weno.py b/example/1d-linear-convection/weno3/julia/02a/python/run_eno_weno.py new file mode 100644 index 00000000..ff46f226 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/python/run_eno_weno.py @@ -0,0 +1,54 @@ +# run_eno_weno.py + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/python/solution.py b/example/1d-linear-convection/weno3/julia/02a/python/solution.py new file mode 100644 index 00000000..92a46d3f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/python/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config.ic_type, config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02a/python/solver.py b/example/1d-linear-convection/weno3/julia/02a/python/solver.py new file mode 100644 index 00000000..fdb5e46a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/python/solver.py @@ -0,0 +1,85 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + + +from flux import InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory + +from boundary import BoundaryCondition, PeriodicBoundary, DirichletBoundary, NeumannBoundary, BoundaryConditionFactory + +from time_integration import TimeIntegrator, RK1Integrator, RK2Integrator, RK3Integrator, TimeIntegratorFactory + +from mesh import Mesh + +from reconstructor import ReconstructorFactory + +from initial_condition import InitialCondition, StepFunctionIC, SineWaveIC, GaussianPulseIC, InitialConditionFactory + +from domain import Domain +from solution import Solution + +from config import CfdConfig +from residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config.ic_type, self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/julia/02a/python/time_integration.py b/example/1d-linear-convection/weno3/julia/02a/python/time_integration.py new file mode 100644 index 00000000..25ac0b4c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02a/python/time_integration.py @@ -0,0 +1,125 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class TimeIntegratorFactory: + """时间推进器工厂""" + + @staticmethod + def create(cfd) -> 'TimeIntegrator': + """ + 创建时间推进器实例 + + Args: + cfd: CFD对象 + + Returns: + 时间推进器实例 + """ + from factories.base_factory import BaseFactory + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02b/boundary.jl b/example/1d-linear-convection/weno3/julia/02b/boundary.jl new file mode 100644 index 00000000..5b0baaf4 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02b/boundary.jl @@ -0,0 +1,90 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02b/config.jl b/example/1d-linear-convection/weno3/julia/02b/config.jl new file mode 100644 index 00000000..bf48de3a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02b/config.jl @@ -0,0 +1,68 @@ +# julia/config.jl +""" +CfdConfig:与 Python config.py 完全同构 +""" +mutable struct CfdConfig + ic_type::String + recon_scheme::String + flux_type::String + rk_order::Int + wave_speed::Float64 + final_time::Float64 + dt::Float64 + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + spatial_order::Int + + function CfdConfig() + new( + "step", + "eno", + "rusanov", + 1, + 1.0, + 0.625, + 0.025, + "periodic", + 1.0, + 2.0, + 2 + ) + end +end + +""" +专用配置:重建方案(链式调用) +""" +function with_reconstruction(cfg::CfdConfig, scheme::String, order::Union{Int, Nothing}=nothing) + cfg.recon_scheme = lowercase(scheme) + + if order !== nothing + cfg.spatial_order = order + else + if startswith(cfg.recon_scheme, "weno") + cfg.spatial_order = 5 + elseif cfg.recon_scheme == "eno" + cfg.spatial_order = 3 + else + error("不支持的重建格式:$scheme(仅支持 eno/weno)") + end + end + + return cfg # 支持链式调用 +end + +""" +专用配置:边界条件(链式调用) +""" +function with_boundary(cfg::CfdConfig, bc_type::String; left_value=nothing, right_value=nothing) + cfg.boundary_type = bc_type + if left_value !== nothing + cfg.left_boundary_value = left_value + end + if right_value !== nothing + cfg.right_boundary_value = right_value + end + return cfg +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02b/domain.jl b/example/1d-linear-convection/weno3/julia/02b/domain.jl new file mode 100644 index 00000000..a7edc226 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02b/domain.jl @@ -0,0 +1,61 @@ +# julia/domain.jl +""" +Domain 结构体(与 domain.py 完全同构) +- 保存 config +- nghosts 计算逻辑 1:1 +- ist/ied 为 0-based(与 Python 一致) +""" + +include("mesh.jl") + +mutable struct Domain + config::Any # 保存 config(与 Python 一致) + mesh::Mesh + nghosts::Int + ist::Int # 0-based + ied::Int # 0-based + ntcells::Int +end + +function Domain(config::Any, mesh::Mesh) + nghosts = _calc_nghosts(config) + ist = nghosts + 1 # ← 1-based 起始索引 + ied = ist + mesh.ncells # ← 1-based 结束索引(不包含) + ntcells = mesh.ncells + 2 * nghosts + Domain(config, mesh, nghosts, ist, ied, ntcells) +end + +function _calc_nghosts(config::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + order = getfield_safe(config, :spatial_order, 2) + + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + if scheme == "eno" + nghosts = order + elseif startswith(scheme, "weno") + nghosts = order ÷ 2 + 1 + else + error("未知重建格式 $(scheme),无法计算ghost层!") + end + + if nghosts <= 0 + error("计算得到的ghost层数量无效:$(nghosts)(阶数$(order),格式$(scheme))") + end + + return nghosts +end + +function is_physical_cell(domain::Domain, idx::Int) + return domain.ist <= idx < domain.ied +end + +function get_physical_indices(domain::Domain) + return domain.ist:(domain.ied - 1) +end + +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02b/flux.jl b/example/1d-linear-convection/weno3/julia/02b/flux.jl new file mode 100644 index 00000000..cff23582 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02b/flux.jl @@ -0,0 +1,112 @@ +# julia/flux.jl +""" +通量计算器模块(与 flux.py 完全同构) +- 抽象基类 + 具体实现 +- 字段:cfd, config, mesh, wave_speed +""" + +include("mesh.jl") + +# ---------------------- 抽象基类 ---------------------- +""" +InviscidFluxCalculator 抽象类型 +Julia 无 ABC,用文档约定 +所有子类型必须实现 compute! +""" +abstract type InviscidFluxCalculator end + +# ---------------------- RusanovFluxCalculator ---------------------- +mutable struct RusanovFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function RusanovFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::RusanovFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = calc.wave_speed + c_R = calc.wave_speed + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + end +end + +# ---------------------- EngquistOsherFluxCalculator ---------------------- +mutable struct EngquistOsherFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function EngquistOsherFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::EngquistOsherFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + c = calc.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + end +end + +# ---------------------- FluxCalculatorFactory ---------------------- +module FluxCalculatorFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "通量计算器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + config = cfd.config + if !hasproperty(config, :flux_type) + error("cfd.config 缺少 flux_type 字段") + end + + flux_type = config.flux_type + if !(flux_type isa AbstractString) + error("flux_type 必须为字符串,当前值: $flux_type") + end + + if !haskey(_REGISTRY, flux_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的通量计算器: '$flux_type'。可用选项: $available") + end + + return _REGISTRY[flux_type](cfd) +end + +# ✅ 修正:使用 Main. 前缀 +register("rusanov", cfd -> Main.RusanovFluxCalculator(cfd)) +register("engquist-osher", cfd -> Main.EngquistOsherFluxCalculator(cfd)) + +end # module FluxCalculatorFactory + +export FluxCalculatorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02b/initial_condition.jl b/example/1d-linear-convection/weno3/julia/02b/initial_condition.jl new file mode 100644 index 00000000..5e029e8d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02b/initial_condition.jl @@ -0,0 +1,86 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02b/mesh.jl b/example/1d-linear-convection/weno3/julia/02b/mesh.jl new file mode 100644 index 00000000..1b0dd4bf --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02b/mesh.jl @@ -0,0 +1,45 @@ +# julia/mesh.jl +""" +Mesh 结构体(与提供的 mesh.py 完全同构) +- 字段名、初始化顺序、循环逻辑完全一致 +- 不做任何泛化或优化 +""" + +mutable struct Mesh + xmin::Float64 + xmax::Float64 + ncells::Int + nnodes::Int + nx::Int + x::Vector{Float64} + xcc::Vector{Float64} + L::Float64 # 在 init_mesh 中赋值 + dx::Float64 # 在 init_mesh 中赋值 + + function Mesh() + # 与 Python __init__ 完全一致 + xmin = 0.0 + xmax = 2.0 + ncells = 40 + nnodes = ncells + 1 + nx = ncells + x = zeros(Float64, nnodes) + xcc = zeros(Float64, ncells) + + # 调用 init_mesh(模拟 Python) + L = xmax - xmin + dx = L / ncells + + # Generate node coordinates: for i in range(self.nnodes) + for i in 1:nnodes + x[i] = xmin + (i - 1) * dx # Python i → Julia i-1 + end + + # Generate cell center coordinates + for i in 1:ncells + xcc[i] = 0.5 * (x[i] + x[i+1]) + end + + new(xmin, xmax, ncells, nnodes, nx, x, xcc, L, dx) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02b/plotter.jl b/example/1d-linear-convection/weno3/julia/02b/plotter.jl new file mode 100644 index 00000000..a77d8d68 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02b/plotter.jl @@ -0,0 +1,147 @@ +# julia/plotter.jl +""" +CFDPlotter 的 Julia 实现(通过 PythonCall.jl 调用 Matplotlib) +确保与 Python plotter.py 行为完全一致 +""" + +using PythonCall + +# 初始化 Python 环境(加载 matplotlib, inflect) +const plt = pyimport("matplotlib.pyplot") +const inflect = pyimport("inflect") + +mutable struct CFDPlotter + default_styles::Dict{String, Any} + p::Py +end + +function CFDPlotter() + default_styles = Dict{String, Any}( + "numerical" => Dict( + :color => "blue", + :linestyle => "-", + :marker => "o", + :markerfacecolor => "none" + ), + "analytical" => Dict( + :color => "red", + :linestyle => "--", + :marker => "", + :linewidth => 1.5 + ), + "comparison" => [ + Dict(:color => "black", :linestyle => "-", :marker => "o", :markerfacecolor => "none"), + Dict(:color => "blue", :linestyle => "--", :marker => "s", :markerfacecolor => "none"), + Dict(:color => "green", :linestyle => ":", :marker => "^", :markerfacecolor => "none") + ] + ) + p = inflect.engine() + CFDPlotter(default_styles, p) +end + +""" +轻量即时绘图(快速验证结果) +""" +function plot_quick(plotter::CFDPlotter, cfd_result::Dict; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + rk_order = cfd_result["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = cfd_result["config"]["final_time"] + order = cfd_result["config"]["order"] + scheme = uppercase(cfd_result["config"]["scheme"]) + actual_title = "1D Convection (t=$(final_time))\n$(order)th-order $(scheme) + $(rk_str)-order RK" + end + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"]; + label="Numerical ($(uppercase(cfd_result["config"]["scheme"])))", + plotter.default_styles["numerical"]..., + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + # 通用样式 + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +""" +多格式/多精度对比绘图 +""" +function plot_comparison(plotter::CFDPlotter, result_list::Vector{Dict{String, Any}}; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + schemes = [uppercase(r["config"]["scheme"]) * string(r["config"]["order"]) for r in result_list] + rk_order = result_list[1]["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = result_list[1]["config"]["final_time"] + actual_title = "1D Convection Comparison (t=$(final_time))\n$(join(schemes, ", ")) + $(rk_str)-order RK" + end + + # 绘制多个数值解 + for (i, res) in enumerate(result_list) + style = plotter.default_styles["comparison"][mod1(i, length(plotter.default_styles["comparison"]))] + label = "Numerical ($(uppercase(res["config"]["scheme"]))$(res["config"]["order"]))" + plt.plot( + res["x"], res["numerical"]; + label=label, + style..., + markersize=5, linewidth=0.5 + ) + end + + # 绘制解析解 + plt.plot( + result_list[1]["x"], result_list[1]["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +function _set_common_style(plotter::CFDPlotter, title::String) + plt.title(title, fontsize=12) + plt.xlabel("x", fontsize=10) + plt.ylabel("u", fontsize=10) + plt.legend(fontsize=9) + plt.grid(true, color="gray", linestyle="--", linewidth=0.5, alpha=0.7) + plt.tight_layout() +end + +""" +快捷函数:ENO/WENO对比绘图 +""" +function plot_eno_weno_comparison(eno_result::Dict, weno_result::Dict; save_path=nothing) + plotter = CFDPlotter() + plot_comparison(plotter, [eno_result, weno_result]; save_path=save_path) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02b/reconstructor/eno.jl b/example/1d-linear-convection/weno3/julia/02b/reconstructor/eno.jl new file mode 100644 index 00000000..e78a636f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02b/reconstructor/eno.jl @@ -0,0 +1,107 @@ +# julia/reconstructor/eno.jl +""" +ENO 重构器(与 reconstructor/eno.py 完全同构) +""" + +# ---------------------- ENO 系数初始化 ---------------------- +function _init_eno_coef!(spatial_order::Int, coef::Matrix{Float64}) + if spatial_order == 1 + coef[1, 1] = 1.0 + coef[2, 1] = 1.0 + elseif spatial_order == 2 + coef[1, 1:2] = [3.0/2.0, -1.0/2.0] + coef[2, 1:2] = [1.0/2.0, 1.0/2.0] + coef[3, 1:2] = [-1.0/2.0, 3.0/2.0] + elseif spatial_order == 3 + coef[1, 1:3] = [11.0/6.0, -7.0/6.0, 1.0/3.0] + coef[2, 1:3] = [1.0/3.0, 5.0/6.0, -1.0/6.0] + coef[3, 1:3] = [-1.0/6.0, 5.0/6.0, 1.0/3.0] + coef[4, 1:3] = [1.0/3.0, -7.0/6.0, 11.0/6.0] + elseif spatial_order == 4 + coef[1, 1:4] = [25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0] + coef[2, 1:4] = [1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0] + coef[3, 1:4] = [-1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0] + coef[4, 1:4] = [1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0] + coef[5, 1:4] = [-1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0] + elseif spatial_order == 5 + coef[1, 1:5] = [137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0] + coef[2, 1:5] = [1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0] + coef[3, 1:5] = [-1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0] + coef[4, 1:5] = [1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0] + coef[5, 1:5] = [-1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0] + coef[6, 1:5] = [1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0] + elseif spatial_order == 6 + coef[1, 1:6] = [49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0] + coef[2, 1:6] = [1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0] + coef[3, 1:6] = [-1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0] + coef[4, 1:6] = [1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0] + coef[5, 1:6] = [-1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0] + coef[6, 1:6] = [1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0] + coef[7, 1:6] = [-1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0] + elseif spatial_order == 7 + coef[1, 1:7] = [363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0] + coef[2, 1:7] = [1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0] + coef[3, 1:7] = [-1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0] + coef[4, 1:7] = [1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0] + coef[5, 1:7] = [-1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0] + coef[6, 1:7] = [1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0] + coef[7, 1:7] = [-1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0] + coef[8, 1:7] = [1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0] + else + error("ENO 系数未实现 order=$spatial_order") + end +end + +# ---------------------- ENO 重构器 ---------------------- +mutable struct EnoReconstructor + spatial_order::Int + ntcells::Int + lmc::Vector{Int} + coef::Matrix{Float64} + dd::Matrix{Float64} + + function EnoReconstructor(spatial_order::Int, ntcells::Int) + lmc = zeros(Int, ntcells) + coef = zeros(Float64, spatial_order + 1, spatial_order) + dd = zeros(Float64, spatial_order, ntcells) + _init_eno_coef!(spatial_order, coef) + new(spatial_order, ntcells, lmc, coef, dd) + end +end + +function reconstruct(rec::EnoReconstructor, q::Vector{Float64}, cfd::Any) + # 1. 差商计算 (dd[1,:] = q) + @views rec.dd[1, :] .= q + for m in 2:rec.spatial_order + for j in 1:(rec.ntcells - m + 1) + rec.dd[m, j] = rec.dd[m-1, j+1] - rec.dd[m-1, j] + end + end + + # 2. 选择 smoothest stencil + domain = cfd.domain + for i in (domain.ist - 1):(domain.ied) # Python: range(ist-1, ied+1) → ied+1-1 = ied + rec.lmc[i] = i + for m in 2:rec.spatial_order + if abs(rec.dd[m, rec.lmc[i] - 1]) < abs(rec.dd[m, rec.lmc[i]]) + rec.lmc[i] -= 1 + end + end + end + + # 3. 重构界面值 + solution = cfd.solution + for i in domain.ist:(domain.ied) # Python: range(ist, ied+1) → ied+1-1 = ied + j = i - domain.ist + 1 # Julia 1-based + k1 = rec.lmc[i - 1] + k2 = rec.lmc[i] + r1 = (i - 1) - k1 + 1 + r2 = i - k2 + 1 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in 1:rec.spatial_order + solution.q_face_left[j] += q[k1 + m - 1] * rec.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m - 1] * rec.coef[r2, m] + end + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02b/reconstructor/weno3.jl b/example/1d-linear-convection/weno3/julia/02b/reconstructor/weno3.jl new file mode 100644 index 00000000..2b6fe1ab --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02b/reconstructor/weno3.jl @@ -0,0 +1,65 @@ +# julia/reconstructor/weno3.jl +""" +WENO3 重构器(与 reconstructor/weno3.py 完全同构) +""" + +mutable struct Weno3Reconstructor + # 无字段,与 Python 一致 +end + +function reconstruct(rec::Weno3Reconstructor, q::Vector{Float64}, cfd::Any) + domain = cfd.domain + solution = cfd.solution + _reconstruct_left_interfaces(domain, q, solution.q_face_left) + _reconstruct_right_interfaces(domain, q, solution.q_face_right) +end + +function _reconstruct_left_interfaces(domain, u, qL) + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in (domain.ist - 1):(domain.ied - 1) + j = i - (domain.ist - 1) + 1 # ← Julia 1-based: j = i - (ist-1) 对应 Python j = i - (ist-1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = _reconstruct_from_left_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_right_interfaces(domain, u, qR) + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in domain.ist:domain.ied + j = i - domain.ist + 1 # ← Julia 1-based: j = i - ist 对应 Python j = i - ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = _reconstruct_from_right_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_from_left_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -0.5*v1 + 1.5*v2 # r=1 stencil + q1 = 0.5*v2 + 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end + +function _reconstruct_from_right_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 0.5*v1 + 0.5*v2 # r=1 stencil + q1 = 1.5*v2 - 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02b/residual.jl b/example/1d-linear-convection/weno3/julia/02b/residual.jl new file mode 100644 index 00000000..e8fd6584 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02b/residual.jl @@ -0,0 +1,65 @@ +# julia/residual.jl +""" +残差计算器(与 residual.py 完全同构) +- 封装重建→通量→散度完整流程 +- 依赖 cfd 的多个字段 +""" + +include("mesh.jl") + +mutable struct ResidualCalculator + cfd::Any + config::Any + domain::Any + solution::Any + mesh::Mesh + reconstructor::Any + flux_calculator::Any # 通量计算器(外部传入,替代工厂) + + function ResidualCalculator(cfd::Any, flux_calculator::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + mesh = domain.mesh + reconstructor = cfd.reconstructor + + new(cfd, config, domain, solution, mesh, reconstructor, flux_calculator) + end +end + +""" +计算完整残差(对外唯一接口) +""" +function compute!(calc::ResidualCalculator) + _reconstruct(calc) + _compute_inviscid_flux(calc) + _compute_flux_divergence(calc) +end + +""" +私有方法:界面值重建 +""" +function _reconstruct(calc::ResidualCalculator) + reconstruct(calc.reconstructor, calc.solution.u, calc.cfd) +end + +""" +私有方法:计算无粘通量 +""" +function _compute_inviscid_flux(calc::ResidualCalculator) + compute!(calc.flux_calculator, + calc.solution.q_face_left, + calc.solution.q_face_right, + calc.solution.flux) +end + +""" +私有方法:计算通量散度(残差 = -dF/dx) +""" +function _compute_flux_divergence(calc::ResidualCalculator) + solution = calc.solution + mesh = calc.mesh + for i in 1:mesh.ncells + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / mesh.dx + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02b/run_eno_weno.jl b/example/1d-linear-convection/weno3/julia/02b/run_eno_weno.jl new file mode 100644 index 00000000..58b6cfa5 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02b/run_eno_weno.jl @@ -0,0 +1,50 @@ +# julia/run_eno_weno.jl +""" +1:1 复刻 run_eno_weno.py 的 Julia 版本 +""" + +include("config.jl") +include("mesh.jl") +include("solver.jl") +include("plotter.jl") + +function performEnoWenoAnalysis() + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + println("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + with_reconstruction(config_eno3, "eno", 3) # 显式指定 3 阶 + config_eno3.dt = 0.0025 # 覆盖默认值 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + run!(cfd_eno3) # 求解并生成 result 字典 + + # 3. 配置并运行 WENO3 求解 + println("Running WENO3 solver...") + config_weno3 = CfdConfig() + with_reconstruction(config_weno3, "weno", 3) # 显式指定 3 阶(WENO 默认 5 阶) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + run!(cfd_weno3) + + # 5. 绘制 ENO/WENO 对比图 + println("Plotting comparison results...") + plot_eno_weno_comparison( + cfd_eno3.result, + cfd_weno3.result; + save_path="eno_weno_comparison.png" + ) + + return cfd_eno3, cfd_weno3 +end + +# 主程序入口 +if abspath(PROGRAM_FILE) == @__FILE__ + performEnoWenoAnalysis() + println("Analysis completed!") +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02b/solution.jl b/example/1d-linear-convection/weno3/julia/02b/solution.jl new file mode 100644 index 00000000..90ca0393 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02b/solution.jl @@ -0,0 +1,75 @@ +# julia/solution.jl +""" +Solution 结构体(与真实 solution.py 完全同构) +字段顺序、方法、逻辑 1:1 对齐 +""" + +include("mesh.jl") +include("domain.jl") +include("initial_condition.jl") + +mutable struct Solution + domain::Domain + q_face_left::Vector{Float64} + q_face_right::Vector{Float64} + flux::Vector{Float64} + res::Vector{Float64} + u::Vector{Float64} + un::Vector{Float64} + + function Solution(config::Any, domain::Domain) + mesh = domain.mesh + + q_face_left = zeros(Float64, mesh.nnodes) + q_face_right = zeros(Float64, mesh.nnodes) + flux = zeros(Float64, mesh.nnodes) + res = zeros(Float64, mesh.ncells) + u = zeros(Float64, domain.ntcells) + un = zeros(Float64, domain.ntcells) + + sol = new(domain, q_face_left, q_face_right, flux, res, u, un) + initialize_from_config(sol, config) + return sol + end +end + +""" +重置解数组为初始状态 +""" +function reset_solution(sol::Solution) + fill!(sol.u, 0.0) + fill!(sol.un, 0.0) +end + +""" +根据配置初始化场 +注:暂用硬编码替代 InitialConditionFactory +""" +function initialize_from_config(sol::Solution, config::Any) + ic_type = getfield_safe(config, :ic_type, "step") + + # 硬编码创建 IC(替代 InitialConditionFactory) + ic = if ic_type == "step" + StepFunctionIC(config) + elseif ic_type == "sin" + SineWaveIC(config) + elseif ic_type == "gaussian" + GaussianPulseIC(config) + else + error("未知初始条件类型: $ic_type") + end + + apply(ic, sol) # 调用 IC.apply +end + +""" +更新旧场:un = u +""" +function update_old_field(sol::Solution) + sol.un .= sol.u # 等价于 Python 的 un[:] = u[:] +end + +# ---------------------- 辅助函数 ---------------------- +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02b/solver.jl b/example/1d-linear-convection/weno3/julia/02b/solver.jl new file mode 100644 index 00000000..8165dcc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02b/solver.jl @@ -0,0 +1,172 @@ +# julia/solver.jl +""" +CFD 求解器主类(与 solver.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("initial_condition.jl") +include("boundary.jl") +include("flux.jl") +include("residual.jl") +include("time_integration.jl") +include("reconstructor/eno.jl") +include("reconstructor/weno3.jl") + +# 导入工厂模块(必须在顶层!) +using .FluxCalculatorFactory + +# ---------------------- Cfd 求解器 ---------------------- +mutable struct Cfd + config::Any + domain::Domain + solution::Solution + reconstructor::Any + residual_calculator::ResidualCalculator + integrator::Any + boundary_condition::Any + result::Dict{String, Any} + + function Cfd(config::Any, mesh::Mesh) + domain = Domain(config, mesh) + solution = Solution(config, domain) + + # 在 Cfd 构造函数中 + # =============== 重建器创建 =============== + recon_scheme = config.recon_scheme + spatial_order = config.spatial_order + + # ✅ 模拟 Python ReconstructorFactory 的逻辑 + if recon_scheme == "weno" + recon_scheme = "weno$(spatial_order)" + end + + # 现在根据 recon_scheme 创建 + reconstructor = if recon_scheme == "eno" + EnoReconstructor(spatial_order, domain.ntcells) + elseif recon_scheme == "weno3" + Weno3Reconstructor() + else + error("不支持的重建格式: $recon_scheme") + end + + # 构造 cfd 上下文(NamedTuple) + cfd_context = ( + config = config, + domain = domain + ) + + # 创建通量计算器 + flux_calculator = FluxCalculatorFactory.create(cfd_context) + + # 残差计算器 + residual_calculator = ResidualCalculator( + (config=config, domain=domain, solution=solution, reconstructor=reconstructor), + flux_calculator + ) + + # 边界条件 + boundary_condition = if config.boundary_type == "periodic" + PeriodicBoundary((config=config, domain=domain)) + elseif config.boundary_type == "dirichlet" + DirichletBoundary((config=config, domain=domain)) + elseif config.boundary_type == "neumann" + NeumannBoundary((config=config, domain=domain)) + else + error("不支持的边界类型: $(config.boundary_type)") + end + + + # 构造用于积分器初始化的上下文对象(NamedTuple,模拟 cfd 接口) + integrator_context = ( + config = config, + domain = domain, + solution = solution, + residual_calculator = residual_calculator, + boundary_condition = boundary_condition + ) + + # 使用注册表工厂创建时间推进器 + integrator = create_integrator(integrator_context) + + #@show typeof(integrator) + + # 注入 cfd 到 residual_calculator 和 integrator + residual_calculator.cfd = (config=config, domain=domain, solution=solution, reconstructor=reconstructor, residual_calculator=residual_calculator, integrator=integrator, boundary_condition=boundary_condition) + integrator.base.cfd = residual_calculator.cfd + + result = Dict{String, Any}() + new(config, domain, solution, reconstructor, residual_calculator, integrator, boundary_condition, result) + end +end + +""" +通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界 +""" +function exact_solution(cfd::Cfd) + x = cfd.domain.mesh.xcc + T = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = @. (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = if cfd.config.ic_type == "step" + StepFunctionIC(cfd.config) + elseif cfd.config.ic_type == "sin" + SineWaveIC(cfd.config) + elseif cfd.config.ic_type == "gaussian" + GaussianPulseIC(cfd.config) + else + error("未知初始条件: $(cfd.config.ic_type)") + end + + return evaluate_at(ic, x_shifted) +end + +""" +主求解循环 +""" +function run!(cfd::Cfd) + # 应用初始边界条件并同步 old field + apply!(cfd.boundary_condition, cfd.solution.u) + update_old_field(cfd.solution) + + t = 0.0 + dt_old = cfd.config.dt + dt = dt_old + + while t < cfd.config.final_time + if t + dt > cfd.config.final_time + dt = cfd.config.final_time - t + end + #@show t, dt, maximum(cfd.solution.u), minimum(cfd.solution.u) + # 执行时间步 + step(cfd.integrator, dt) + t += dt + end + + # 恢复 dt + cfd.config.dt = dt_old + + # 整理结果 + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied-1] # Python: [ist:ied] + analytical = exact_solution(cfd) + + cfd.result = Dict( + "x" => cfd.domain.mesh.xcc, + "numerical" => u_numerical, + "analytical" => analytical, + "config" => Dict( + "scheme" => cfd.config.recon_scheme, + "order" => cfd.config.spatial_order, + "rk_order" => cfd.config.rk_order, + "final_time" => cfd.config.final_time + ) + ) + + return u_numerical +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02b/time_integration.jl b/example/1d-linear-convection/weno3/julia/02b/time_integration.jl new file mode 100644 index 00000000..43b540af --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02b/time_integration.jl @@ -0,0 +1,168 @@ +# julia/time_integration.jl +""" +时间推进器模块(与 time_integration.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("residual.jl") +include("boundary.jl") + +# ---------------------- 抽象时间推进器基类 ---------------------- +abstract type TimeIntegrator end + +mutable struct TimeIntegratorBase <: TimeIntegrator + cfd::Any + config::Any + domain::Domain + solution::Solution + residual_calculator::Any # ResidualCalculator +end + +function TimeIntegratorBase(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + residual_calculator = cfd.residual_calculator + TimeIntegratorBase(cfd, config, domain, solution, residual_calculator) +end + +function compute_residual(integrator::TimeIntegratorBase) + compute!(integrator.residual_calculator) +end + +function apply_boundary(integrator::TimeIntegratorBase) + apply!(integrator.cfd.boundary_condition, integrator.solution.u) +end + +function map_idx(integrator::TimeIntegratorBase, i::Int) + return i - integrator.domain.ist + 1 # ← +1 转为 1-based +end + +# ---------------------- RK1Integrator ---------------------- +mutable struct RK1Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK1Integrator(cfd::Any) + RK1Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK1Integrator, dt::Float64) + compute_residual(integrator.base) + for i in integrator.base.domain.ist:(integrator.base.domain.ied - 1) + j = map_idx(integrator.base, i) + integrator.base.solution.u[i] += dt * integrator.base.solution.res[j] + end + apply_boundary(integrator.base) + update_old_field(integrator.base.solution) +end + +# ---------------------- RK2Integrator ---------------------- +mutable struct RK2Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK2Integrator(cfd::Any) + RK2Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK2Integrator, dt::Float64) + base = integrator.base + # 阶段1:预测步 + compute_residual(base) + u_pred = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u_pred[i] += dt * base.solution.res[j] + end + base.solution.u .= u_pred + apply_boundary(base) + # 阶段2:校正步 + compute_residual(base) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = 0.5 * base.solution.un[i] + 0.5 * base.solution.u[i] + 0.5 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- RK3Integrator ---------------------- +mutable struct RK3Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK3Integrator(cfd::Any) + RK3Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK3Integrator, dt::Float64) + base = integrator.base + # 阶段1 + compute_residual(base) + u1 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u1[i] += dt * base.solution.res[j] + end + base.solution.u .= u1 + apply_boundary(base) + # 阶段2 + compute_residual(base) + u2 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u2[i] = 0.75 * base.solution.un[i] + 0.25 * base.solution.u[i] + 0.25 * dt * base.solution.res[j] + end + base.solution.u .= u2 + apply_boundary(base) + # 阶段3 + compute_residual(base) + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = c1 * base.solution.un[i] + c2 * base.solution.u[i] + c3 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- 注册表 + 工厂(方案2)---------------------- + +const INTEGRATOR_REGISTRY = Dict{String, Function}() + +function register_integrator(name::String, ctor::Function) + if haskey(INTEGRATOR_REGISTRY, name) + @warn "积分器 '$name' 已注册,将被覆盖" + end + INTEGRATOR_REGISTRY[name] = ctor +end + +function create_integrator(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + config = cfd.config + if !hasproperty(config, :rk_order) + error("cfd.config 缺少 rk_order 字段") + end + rk_order = config.rk_order + if !(rk_order isa Integer) || rk_order < 1 + error("rk_order 必须为正整数,当前值: $rk_order (类型: $(typeof(rk_order)))") + end + + name = "rk$rk_order" + if !haskey(INTEGRATOR_REGISTRY, name) + available = sort(collect(keys(INTEGRATOR_REGISTRY))) + error("未注册的时间积分器: '$name'。可用选项: $available") + end + + return INTEGRATOR_REGISTRY[name](cfd) +end + +# 注册内置积分器 +register_integrator("rk1", cfd -> RK1Integrator(cfd)) +register_integrator("rk2", cfd -> RK2Integrator(cfd)) +register_integrator("rk3", cfd -> RK3Integrator(cfd)) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02c/boundary.jl b/example/1d-linear-convection/weno3/julia/02c/boundary.jl new file mode 100644 index 00000000..5b0baaf4 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02c/boundary.jl @@ -0,0 +1,90 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02c/config.jl b/example/1d-linear-convection/weno3/julia/02c/config.jl new file mode 100644 index 00000000..bf48de3a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02c/config.jl @@ -0,0 +1,68 @@ +# julia/config.jl +""" +CfdConfig:与 Python config.py 完全同构 +""" +mutable struct CfdConfig + ic_type::String + recon_scheme::String + flux_type::String + rk_order::Int + wave_speed::Float64 + final_time::Float64 + dt::Float64 + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + spatial_order::Int + + function CfdConfig() + new( + "step", + "eno", + "rusanov", + 1, + 1.0, + 0.625, + 0.025, + "periodic", + 1.0, + 2.0, + 2 + ) + end +end + +""" +专用配置:重建方案(链式调用) +""" +function with_reconstruction(cfg::CfdConfig, scheme::String, order::Union{Int, Nothing}=nothing) + cfg.recon_scheme = lowercase(scheme) + + if order !== nothing + cfg.spatial_order = order + else + if startswith(cfg.recon_scheme, "weno") + cfg.spatial_order = 5 + elseif cfg.recon_scheme == "eno" + cfg.spatial_order = 3 + else + error("不支持的重建格式:$scheme(仅支持 eno/weno)") + end + end + + return cfg # 支持链式调用 +end + +""" +专用配置:边界条件(链式调用) +""" +function with_boundary(cfg::CfdConfig, bc_type::String; left_value=nothing, right_value=nothing) + cfg.boundary_type = bc_type + if left_value !== nothing + cfg.left_boundary_value = left_value + end + if right_value !== nothing + cfg.right_boundary_value = right_value + end + return cfg +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02c/domain.jl b/example/1d-linear-convection/weno3/julia/02c/domain.jl new file mode 100644 index 00000000..a7edc226 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02c/domain.jl @@ -0,0 +1,61 @@ +# julia/domain.jl +""" +Domain 结构体(与 domain.py 完全同构) +- 保存 config +- nghosts 计算逻辑 1:1 +- ist/ied 为 0-based(与 Python 一致) +""" + +include("mesh.jl") + +mutable struct Domain + config::Any # 保存 config(与 Python 一致) + mesh::Mesh + nghosts::Int + ist::Int # 0-based + ied::Int # 0-based + ntcells::Int +end + +function Domain(config::Any, mesh::Mesh) + nghosts = _calc_nghosts(config) + ist = nghosts + 1 # ← 1-based 起始索引 + ied = ist + mesh.ncells # ← 1-based 结束索引(不包含) + ntcells = mesh.ncells + 2 * nghosts + Domain(config, mesh, nghosts, ist, ied, ntcells) +end + +function _calc_nghosts(config::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + order = getfield_safe(config, :spatial_order, 2) + + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + if scheme == "eno" + nghosts = order + elseif startswith(scheme, "weno") + nghosts = order ÷ 2 + 1 + else + error("未知重建格式 $(scheme),无法计算ghost层!") + end + + if nghosts <= 0 + error("计算得到的ghost层数量无效:$(nghosts)(阶数$(order),格式$(scheme))") + end + + return nghosts +end + +function is_physical_cell(domain::Domain, idx::Int) + return domain.ist <= idx < domain.ied +end + +function get_physical_indices(domain::Domain) + return domain.ist:(domain.ied - 1) +end + +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02c/flux.jl b/example/1d-linear-convection/weno3/julia/02c/flux.jl new file mode 100644 index 00000000..cff23582 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02c/flux.jl @@ -0,0 +1,112 @@ +# julia/flux.jl +""" +通量计算器模块(与 flux.py 完全同构) +- 抽象基类 + 具体实现 +- 字段:cfd, config, mesh, wave_speed +""" + +include("mesh.jl") + +# ---------------------- 抽象基类 ---------------------- +""" +InviscidFluxCalculator 抽象类型 +Julia 无 ABC,用文档约定 +所有子类型必须实现 compute! +""" +abstract type InviscidFluxCalculator end + +# ---------------------- RusanovFluxCalculator ---------------------- +mutable struct RusanovFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function RusanovFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::RusanovFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = calc.wave_speed + c_R = calc.wave_speed + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + end +end + +# ---------------------- EngquistOsherFluxCalculator ---------------------- +mutable struct EngquistOsherFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function EngquistOsherFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::EngquistOsherFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + c = calc.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + end +end + +# ---------------------- FluxCalculatorFactory ---------------------- +module FluxCalculatorFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "通量计算器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + config = cfd.config + if !hasproperty(config, :flux_type) + error("cfd.config 缺少 flux_type 字段") + end + + flux_type = config.flux_type + if !(flux_type isa AbstractString) + error("flux_type 必须为字符串,当前值: $flux_type") + end + + if !haskey(_REGISTRY, flux_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的通量计算器: '$flux_type'。可用选项: $available") + end + + return _REGISTRY[flux_type](cfd) +end + +# ✅ 修正:使用 Main. 前缀 +register("rusanov", cfd -> Main.RusanovFluxCalculator(cfd)) +register("engquist-osher", cfd -> Main.EngquistOsherFluxCalculator(cfd)) + +end # module FluxCalculatorFactory + +export FluxCalculatorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02c/initial_condition.jl b/example/1d-linear-convection/weno3/julia/02c/initial_condition.jl new file mode 100644 index 00000000..5e029e8d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02c/initial_condition.jl @@ -0,0 +1,86 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02c/mesh.jl b/example/1d-linear-convection/weno3/julia/02c/mesh.jl new file mode 100644 index 00000000..1b0dd4bf --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02c/mesh.jl @@ -0,0 +1,45 @@ +# julia/mesh.jl +""" +Mesh 结构体(与提供的 mesh.py 完全同构) +- 字段名、初始化顺序、循环逻辑完全一致 +- 不做任何泛化或优化 +""" + +mutable struct Mesh + xmin::Float64 + xmax::Float64 + ncells::Int + nnodes::Int + nx::Int + x::Vector{Float64} + xcc::Vector{Float64} + L::Float64 # 在 init_mesh 中赋值 + dx::Float64 # 在 init_mesh 中赋值 + + function Mesh() + # 与 Python __init__ 完全一致 + xmin = 0.0 + xmax = 2.0 + ncells = 40 + nnodes = ncells + 1 + nx = ncells + x = zeros(Float64, nnodes) + xcc = zeros(Float64, ncells) + + # 调用 init_mesh(模拟 Python) + L = xmax - xmin + dx = L / ncells + + # Generate node coordinates: for i in range(self.nnodes) + for i in 1:nnodes + x[i] = xmin + (i - 1) * dx # Python i → Julia i-1 + end + + # Generate cell center coordinates + for i in 1:ncells + xcc[i] = 0.5 * (x[i] + x[i+1]) + end + + new(xmin, xmax, ncells, nnodes, nx, x, xcc, L, dx) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02c/plotter.jl b/example/1d-linear-convection/weno3/julia/02c/plotter.jl new file mode 100644 index 00000000..a77d8d68 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02c/plotter.jl @@ -0,0 +1,147 @@ +# julia/plotter.jl +""" +CFDPlotter 的 Julia 实现(通过 PythonCall.jl 调用 Matplotlib) +确保与 Python plotter.py 行为完全一致 +""" + +using PythonCall + +# 初始化 Python 环境(加载 matplotlib, inflect) +const plt = pyimport("matplotlib.pyplot") +const inflect = pyimport("inflect") + +mutable struct CFDPlotter + default_styles::Dict{String, Any} + p::Py +end + +function CFDPlotter() + default_styles = Dict{String, Any}( + "numerical" => Dict( + :color => "blue", + :linestyle => "-", + :marker => "o", + :markerfacecolor => "none" + ), + "analytical" => Dict( + :color => "red", + :linestyle => "--", + :marker => "", + :linewidth => 1.5 + ), + "comparison" => [ + Dict(:color => "black", :linestyle => "-", :marker => "o", :markerfacecolor => "none"), + Dict(:color => "blue", :linestyle => "--", :marker => "s", :markerfacecolor => "none"), + Dict(:color => "green", :linestyle => ":", :marker => "^", :markerfacecolor => "none") + ] + ) + p = inflect.engine() + CFDPlotter(default_styles, p) +end + +""" +轻量即时绘图(快速验证结果) +""" +function plot_quick(plotter::CFDPlotter, cfd_result::Dict; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + rk_order = cfd_result["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = cfd_result["config"]["final_time"] + order = cfd_result["config"]["order"] + scheme = uppercase(cfd_result["config"]["scheme"]) + actual_title = "1D Convection (t=$(final_time))\n$(order)th-order $(scheme) + $(rk_str)-order RK" + end + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"]; + label="Numerical ($(uppercase(cfd_result["config"]["scheme"])))", + plotter.default_styles["numerical"]..., + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + # 通用样式 + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +""" +多格式/多精度对比绘图 +""" +function plot_comparison(plotter::CFDPlotter, result_list::Vector{Dict{String, Any}}; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + schemes = [uppercase(r["config"]["scheme"]) * string(r["config"]["order"]) for r in result_list] + rk_order = result_list[1]["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = result_list[1]["config"]["final_time"] + actual_title = "1D Convection Comparison (t=$(final_time))\n$(join(schemes, ", ")) + $(rk_str)-order RK" + end + + # 绘制多个数值解 + for (i, res) in enumerate(result_list) + style = plotter.default_styles["comparison"][mod1(i, length(plotter.default_styles["comparison"]))] + label = "Numerical ($(uppercase(res["config"]["scheme"]))$(res["config"]["order"]))" + plt.plot( + res["x"], res["numerical"]; + label=label, + style..., + markersize=5, linewidth=0.5 + ) + end + + # 绘制解析解 + plt.plot( + result_list[1]["x"], result_list[1]["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +function _set_common_style(plotter::CFDPlotter, title::String) + plt.title(title, fontsize=12) + plt.xlabel("x", fontsize=10) + plt.ylabel("u", fontsize=10) + plt.legend(fontsize=9) + plt.grid(true, color="gray", linestyle="--", linewidth=0.5, alpha=0.7) + plt.tight_layout() +end + +""" +快捷函数:ENO/WENO对比绘图 +""" +function plot_eno_weno_comparison(eno_result::Dict, weno_result::Dict; save_path=nothing) + plotter = CFDPlotter() + plot_comparison(plotter, [eno_result, weno_result]; save_path=save_path) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02c/reconstructor/eno.jl b/example/1d-linear-convection/weno3/julia/02c/reconstructor/eno.jl new file mode 100644 index 00000000..e78a636f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02c/reconstructor/eno.jl @@ -0,0 +1,107 @@ +# julia/reconstructor/eno.jl +""" +ENO 重构器(与 reconstructor/eno.py 完全同构) +""" + +# ---------------------- ENO 系数初始化 ---------------------- +function _init_eno_coef!(spatial_order::Int, coef::Matrix{Float64}) + if spatial_order == 1 + coef[1, 1] = 1.0 + coef[2, 1] = 1.0 + elseif spatial_order == 2 + coef[1, 1:2] = [3.0/2.0, -1.0/2.0] + coef[2, 1:2] = [1.0/2.0, 1.0/2.0] + coef[3, 1:2] = [-1.0/2.0, 3.0/2.0] + elseif spatial_order == 3 + coef[1, 1:3] = [11.0/6.0, -7.0/6.0, 1.0/3.0] + coef[2, 1:3] = [1.0/3.0, 5.0/6.0, -1.0/6.0] + coef[3, 1:3] = [-1.0/6.0, 5.0/6.0, 1.0/3.0] + coef[4, 1:3] = [1.0/3.0, -7.0/6.0, 11.0/6.0] + elseif spatial_order == 4 + coef[1, 1:4] = [25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0] + coef[2, 1:4] = [1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0] + coef[3, 1:4] = [-1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0] + coef[4, 1:4] = [1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0] + coef[5, 1:4] = [-1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0] + elseif spatial_order == 5 + coef[1, 1:5] = [137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0] + coef[2, 1:5] = [1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0] + coef[3, 1:5] = [-1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0] + coef[4, 1:5] = [1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0] + coef[5, 1:5] = [-1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0] + coef[6, 1:5] = [1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0] + elseif spatial_order == 6 + coef[1, 1:6] = [49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0] + coef[2, 1:6] = [1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0] + coef[3, 1:6] = [-1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0] + coef[4, 1:6] = [1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0] + coef[5, 1:6] = [-1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0] + coef[6, 1:6] = [1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0] + coef[7, 1:6] = [-1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0] + elseif spatial_order == 7 + coef[1, 1:7] = [363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0] + coef[2, 1:7] = [1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0] + coef[3, 1:7] = [-1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0] + coef[4, 1:7] = [1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0] + coef[5, 1:7] = [-1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0] + coef[6, 1:7] = [1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0] + coef[7, 1:7] = [-1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0] + coef[8, 1:7] = [1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0] + else + error("ENO 系数未实现 order=$spatial_order") + end +end + +# ---------------------- ENO 重构器 ---------------------- +mutable struct EnoReconstructor + spatial_order::Int + ntcells::Int + lmc::Vector{Int} + coef::Matrix{Float64} + dd::Matrix{Float64} + + function EnoReconstructor(spatial_order::Int, ntcells::Int) + lmc = zeros(Int, ntcells) + coef = zeros(Float64, spatial_order + 1, spatial_order) + dd = zeros(Float64, spatial_order, ntcells) + _init_eno_coef!(spatial_order, coef) + new(spatial_order, ntcells, lmc, coef, dd) + end +end + +function reconstruct(rec::EnoReconstructor, q::Vector{Float64}, cfd::Any) + # 1. 差商计算 (dd[1,:] = q) + @views rec.dd[1, :] .= q + for m in 2:rec.spatial_order + for j in 1:(rec.ntcells - m + 1) + rec.dd[m, j] = rec.dd[m-1, j+1] - rec.dd[m-1, j] + end + end + + # 2. 选择 smoothest stencil + domain = cfd.domain + for i in (domain.ist - 1):(domain.ied) # Python: range(ist-1, ied+1) → ied+1-1 = ied + rec.lmc[i] = i + for m in 2:rec.spatial_order + if abs(rec.dd[m, rec.lmc[i] - 1]) < abs(rec.dd[m, rec.lmc[i]]) + rec.lmc[i] -= 1 + end + end + end + + # 3. 重构界面值 + solution = cfd.solution + for i in domain.ist:(domain.ied) # Python: range(ist, ied+1) → ied+1-1 = ied + j = i - domain.ist + 1 # Julia 1-based + k1 = rec.lmc[i - 1] + k2 = rec.lmc[i] + r1 = (i - 1) - k1 + 1 + r2 = i - k2 + 1 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in 1:rec.spatial_order + solution.q_face_left[j] += q[k1 + m - 1] * rec.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m - 1] * rec.coef[r2, m] + end + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02c/reconstructor/weno3.jl b/example/1d-linear-convection/weno3/julia/02c/reconstructor/weno3.jl new file mode 100644 index 00000000..2b6fe1ab --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02c/reconstructor/weno3.jl @@ -0,0 +1,65 @@ +# julia/reconstructor/weno3.jl +""" +WENO3 重构器(与 reconstructor/weno3.py 完全同构) +""" + +mutable struct Weno3Reconstructor + # 无字段,与 Python 一致 +end + +function reconstruct(rec::Weno3Reconstructor, q::Vector{Float64}, cfd::Any) + domain = cfd.domain + solution = cfd.solution + _reconstruct_left_interfaces(domain, q, solution.q_face_left) + _reconstruct_right_interfaces(domain, q, solution.q_face_right) +end + +function _reconstruct_left_interfaces(domain, u, qL) + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in (domain.ist - 1):(domain.ied - 1) + j = i - (domain.ist - 1) + 1 # ← Julia 1-based: j = i - (ist-1) 对应 Python j = i - (ist-1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = _reconstruct_from_left_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_right_interfaces(domain, u, qR) + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in domain.ist:domain.ied + j = i - domain.ist + 1 # ← Julia 1-based: j = i - ist 对应 Python j = i - ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = _reconstruct_from_right_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_from_left_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -0.5*v1 + 1.5*v2 # r=1 stencil + q1 = 0.5*v2 + 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end + +function _reconstruct_from_right_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 0.5*v1 + 0.5*v2 # r=1 stencil + q1 = 1.5*v2 - 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02c/residual.jl b/example/1d-linear-convection/weno3/julia/02c/residual.jl new file mode 100644 index 00000000..e8fd6584 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02c/residual.jl @@ -0,0 +1,65 @@ +# julia/residual.jl +""" +残差计算器(与 residual.py 完全同构) +- 封装重建→通量→散度完整流程 +- 依赖 cfd 的多个字段 +""" + +include("mesh.jl") + +mutable struct ResidualCalculator + cfd::Any + config::Any + domain::Any + solution::Any + mesh::Mesh + reconstructor::Any + flux_calculator::Any # 通量计算器(外部传入,替代工厂) + + function ResidualCalculator(cfd::Any, flux_calculator::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + mesh = domain.mesh + reconstructor = cfd.reconstructor + + new(cfd, config, domain, solution, mesh, reconstructor, flux_calculator) + end +end + +""" +计算完整残差(对外唯一接口) +""" +function compute!(calc::ResidualCalculator) + _reconstruct(calc) + _compute_inviscid_flux(calc) + _compute_flux_divergence(calc) +end + +""" +私有方法:界面值重建 +""" +function _reconstruct(calc::ResidualCalculator) + reconstruct(calc.reconstructor, calc.solution.u, calc.cfd) +end + +""" +私有方法:计算无粘通量 +""" +function _compute_inviscid_flux(calc::ResidualCalculator) + compute!(calc.flux_calculator, + calc.solution.q_face_left, + calc.solution.q_face_right, + calc.solution.flux) +end + +""" +私有方法:计算通量散度(残差 = -dF/dx) +""" +function _compute_flux_divergence(calc::ResidualCalculator) + solution = calc.solution + mesh = calc.mesh + for i in 1:mesh.ncells + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / mesh.dx + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02c/run_eno_weno.jl b/example/1d-linear-convection/weno3/julia/02c/run_eno_weno.jl new file mode 100644 index 00000000..58b6cfa5 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02c/run_eno_weno.jl @@ -0,0 +1,50 @@ +# julia/run_eno_weno.jl +""" +1:1 复刻 run_eno_weno.py 的 Julia 版本 +""" + +include("config.jl") +include("mesh.jl") +include("solver.jl") +include("plotter.jl") + +function performEnoWenoAnalysis() + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + println("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + with_reconstruction(config_eno3, "eno", 3) # 显式指定 3 阶 + config_eno3.dt = 0.0025 # 覆盖默认值 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + run!(cfd_eno3) # 求解并生成 result 字典 + + # 3. 配置并运行 WENO3 求解 + println("Running WENO3 solver...") + config_weno3 = CfdConfig() + with_reconstruction(config_weno3, "weno", 3) # 显式指定 3 阶(WENO 默认 5 阶) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + run!(cfd_weno3) + + # 5. 绘制 ENO/WENO 对比图 + println("Plotting comparison results...") + plot_eno_weno_comparison( + cfd_eno3.result, + cfd_weno3.result; + save_path="eno_weno_comparison.png" + ) + + return cfd_eno3, cfd_weno3 +end + +# 主程序入口 +if abspath(PROGRAM_FILE) == @__FILE__ + performEnoWenoAnalysis() + println("Analysis completed!") +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02c/solution.jl b/example/1d-linear-convection/weno3/julia/02c/solution.jl new file mode 100644 index 00000000..90ca0393 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02c/solution.jl @@ -0,0 +1,75 @@ +# julia/solution.jl +""" +Solution 结构体(与真实 solution.py 完全同构) +字段顺序、方法、逻辑 1:1 对齐 +""" + +include("mesh.jl") +include("domain.jl") +include("initial_condition.jl") + +mutable struct Solution + domain::Domain + q_face_left::Vector{Float64} + q_face_right::Vector{Float64} + flux::Vector{Float64} + res::Vector{Float64} + u::Vector{Float64} + un::Vector{Float64} + + function Solution(config::Any, domain::Domain) + mesh = domain.mesh + + q_face_left = zeros(Float64, mesh.nnodes) + q_face_right = zeros(Float64, mesh.nnodes) + flux = zeros(Float64, mesh.nnodes) + res = zeros(Float64, mesh.ncells) + u = zeros(Float64, domain.ntcells) + un = zeros(Float64, domain.ntcells) + + sol = new(domain, q_face_left, q_face_right, flux, res, u, un) + initialize_from_config(sol, config) + return sol + end +end + +""" +重置解数组为初始状态 +""" +function reset_solution(sol::Solution) + fill!(sol.u, 0.0) + fill!(sol.un, 0.0) +end + +""" +根据配置初始化场 +注:暂用硬编码替代 InitialConditionFactory +""" +function initialize_from_config(sol::Solution, config::Any) + ic_type = getfield_safe(config, :ic_type, "step") + + # 硬编码创建 IC(替代 InitialConditionFactory) + ic = if ic_type == "step" + StepFunctionIC(config) + elseif ic_type == "sin" + SineWaveIC(config) + elseif ic_type == "gaussian" + GaussianPulseIC(config) + else + error("未知初始条件类型: $ic_type") + end + + apply(ic, sol) # 调用 IC.apply +end + +""" +更新旧场:un = u +""" +function update_old_field(sol::Solution) + sol.un .= sol.u # 等价于 Python 的 un[:] = u[:] +end + +# ---------------------- 辅助函数 ---------------------- +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02c/solver.jl b/example/1d-linear-convection/weno3/julia/02c/solver.jl new file mode 100644 index 00000000..c45aa897 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02c/solver.jl @@ -0,0 +1,171 @@ +# julia/solver.jl +""" +CFD 求解器主类(与 solver.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("initial_condition.jl") +include("boundary.jl") +include("flux.jl") +include("residual.jl") +include("time_integration.jl") +include("reconstructor/eno.jl") +include("reconstructor/weno3.jl") + +# 导入工厂模块(必须在顶层!) +using .FluxCalculatorFactory +using .TimeIntegratorFactory + +# ---------------------- Cfd 求解器 ---------------------- +mutable struct Cfd + config::Any + domain::Domain + solution::Solution + reconstructor::Any + residual_calculator::ResidualCalculator + integrator::Any + boundary_condition::Any + result::Dict{String, Any} + + function Cfd(config::Any, mesh::Mesh) + domain = Domain(config, mesh) + solution = Solution(config, domain) + + # 在 Cfd 构造函数中 + # =============== 重建器创建 =============== + recon_scheme = config.recon_scheme + spatial_order = config.spatial_order + + # ✅ 模拟 Python ReconstructorFactory 的逻辑 + if recon_scheme == "weno" + recon_scheme = "weno$(spatial_order)" + end + + # 现在根据 recon_scheme 创建 + reconstructor = if recon_scheme == "eno" + EnoReconstructor(spatial_order, domain.ntcells) + elseif recon_scheme == "weno3" + Weno3Reconstructor() + else + error("不支持的重建格式: $recon_scheme") + end + + # 构造 cfd 上下文(NamedTuple) + cfd_context = ( + config = config, + domain = domain + ) + + # 创建通量计算器 + flux_calculator = FluxCalculatorFactory.create(cfd_context) + + # 残差计算器 + residual_calculator = ResidualCalculator( + (config=config, domain=domain, solution=solution, reconstructor=reconstructor), + flux_calculator + ) + + # 边界条件 + boundary_condition = if config.boundary_type == "periodic" + PeriodicBoundary((config=config, domain=domain)) + elseif config.boundary_type == "dirichlet" + DirichletBoundary((config=config, domain=domain)) + elseif config.boundary_type == "neumann" + NeumannBoundary((config=config, domain=domain)) + else + error("不支持的边界类型: $(config.boundary_type)") + end + + # 构建时间推进器所需上下文 + integrator_context = ( + config = config, + domain = domain, + solution = solution, + residual_calculator = residual_calculator, + boundary_condition = boundary_condition + ) + + # 使用工厂创建时间推进器 + integrator = TimeIntegratorFactory.create(integrator_context) + #@show typeof(integrator) + + # 注入 cfd 到 residual_calculator 和 integrator + residual_calculator.cfd = (config=config, domain=domain, solution=solution, reconstructor=reconstructor, residual_calculator=residual_calculator, integrator=integrator, boundary_condition=boundary_condition) + integrator.base.cfd = residual_calculator.cfd + + result = Dict{String, Any}() + new(config, domain, solution, reconstructor, residual_calculator, integrator, boundary_condition, result) + end +end + +""" +通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界 +""" +function exact_solution(cfd::Cfd) + x = cfd.domain.mesh.xcc + T = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = @. (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = if cfd.config.ic_type == "step" + StepFunctionIC(cfd.config) + elseif cfd.config.ic_type == "sin" + SineWaveIC(cfd.config) + elseif cfd.config.ic_type == "gaussian" + GaussianPulseIC(cfd.config) + else + error("未知初始条件: $(cfd.config.ic_type)") + end + + return evaluate_at(ic, x_shifted) +end + +""" +主求解循环 +""" +function run!(cfd::Cfd) + # 应用初始边界条件并同步 old field + apply!(cfd.boundary_condition, cfd.solution.u) + update_old_field(cfd.solution) + + t = 0.0 + dt_old = cfd.config.dt + dt = dt_old + + while t < cfd.config.final_time + if t + dt > cfd.config.final_time + dt = cfd.config.final_time - t + end + #@show t, dt, maximum(cfd.solution.u), minimum(cfd.solution.u) + # 执行时间步 + step(cfd.integrator, dt) + t += dt + end + + # 恢复 dt + cfd.config.dt = dt_old + + # 整理结果 + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied-1] # Python: [ist:ied] + analytical = exact_solution(cfd) + + cfd.result = Dict( + "x" => cfd.domain.mesh.xcc, + "numerical" => u_numerical, + "analytical" => analytical, + "config" => Dict( + "scheme" => cfd.config.recon_scheme, + "order" => cfd.config.spatial_order, + "rk_order" => cfd.config.rk_order, + "final_time" => cfd.config.final_time + ) + ) + + return u_numerical +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02c/time_integration.jl b/example/1d-linear-convection/weno3/julia/02c/time_integration.jl new file mode 100644 index 00000000..add564ba --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02c/time_integration.jl @@ -0,0 +1,169 @@ +# julia/time_integration.jl +""" +时间推进器模块(与 time_integration.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("residual.jl") +include("boundary.jl") + +# ---------------------- 抽象时间推进器基类 ---------------------- +abstract type TimeIntegrator end + +mutable struct TimeIntegratorBase <: TimeIntegrator + cfd::Any + config::Any + domain::Domain + solution::Solution + residual_calculator::Any # ResidualCalculator +end + +function TimeIntegratorBase(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + residual_calculator = cfd.residual_calculator + TimeIntegratorBase(cfd, config, domain, solution, residual_calculator) +end + +function compute_residual(integrator::TimeIntegratorBase) + compute!(integrator.residual_calculator) +end + +function apply_boundary(integrator::TimeIntegratorBase) + apply!(integrator.cfd.boundary_condition, integrator.solution.u) +end + +function map_idx(integrator::TimeIntegratorBase, i::Int) + return i - integrator.domain.ist + 1 # ← +1 转为 1-based +end + +# ---------------------- RK1Integrator ---------------------- +mutable struct RK1Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK1Integrator(cfd::Any) + RK1Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK1Integrator, dt::Float64) + compute_residual(integrator.base) + for i in integrator.base.domain.ist:(integrator.base.domain.ied - 1) + j = map_idx(integrator.base, i) + integrator.base.solution.u[i] += dt * integrator.base.solution.res[j] + end + apply_boundary(integrator.base) + update_old_field(integrator.base.solution) +end + +# ---------------------- RK2Integrator ---------------------- +mutable struct RK2Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK2Integrator(cfd::Any) + RK2Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK2Integrator, dt::Float64) + base = integrator.base + # 阶段1:预测步 + compute_residual(base) + u_pred = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u_pred[i] += dt * base.solution.res[j] + end + base.solution.u .= u_pred + apply_boundary(base) + # 阶段2:校正步 + compute_residual(base) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = 0.5 * base.solution.un[i] + 0.5 * base.solution.u[i] + 0.5 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- RK3Integrator ---------------------- +mutable struct RK3Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK3Integrator(cfd::Any) + RK3Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK3Integrator, dt::Float64) + base = integrator.base + # 阶段1 + compute_residual(base) + u1 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u1[i] += dt * base.solution.res[j] + end + base.solution.u .= u1 + apply_boundary(base) + # 阶段2 + compute_residual(base) + u2 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u2[i] = 0.75 * base.solution.un[i] + 0.25 * base.solution.u[i] + 0.25 * dt * base.solution.res[j] + end + base.solution.u .= u2 + apply_boundary(base) + # 阶段3 + compute_residual(base) + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = c1 * base.solution.un[i] + c2 * base.solution.u[i] + c3 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- TimeIntegratorFactory ---------------------- +module TimeIntegratorFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "时间积分器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + rk_order = cfd.config.rk_order + if !(rk_order isa Integer) || rk_order < 1 + error("rk_order 必须为正整数,当前值: $rk_order") + end + + name = "rk$rk_order" + if !haskey(_REGISTRY, name) + available = sort(collect(keys(_REGISTRY))) + error("未注册的时间积分器: '$name'。可用选项: $available") + end + + return _REGISTRY[name](cfd) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("rk1", cfd -> Main.RK1Integrator(cfd)) +register("rk2", cfd -> Main.RK2Integrator(cfd)) +register("rk3", cfd -> Main.RK3Integrator(cfd)) + +end # module TimeIntegratorFactory + +export TimeIntegratorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02d/boundary.jl b/example/1d-linear-convection/weno3/julia/02d/boundary.jl new file mode 100644 index 00000000..396c1264 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02d/boundary.jl @@ -0,0 +1,129 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end + + +# ---------------------- BoundaryConditionFactory ---------------------- +module BoundaryConditionFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "边界条件 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + bc_type = cfd.config.boundary_type + if !(bc_type isa AbstractString) + error("boundary_type 必须为字符串,当前值: $bc_type") + end + + if !haskey(_REGISTRY, bc_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的边界条件: '$bc_type'。可用选项: $available") + end + + return _REGISTRY[bc_type](cfd) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("periodic", cfd -> Main.PeriodicBoundary(cfd)) +register("dirichlet", cfd -> Main.DirichletBoundary(cfd)) +register("neumann", cfd -> Main.NeumannBoundary(cfd)) + +end # module BoundaryConditionFactory + +export BoundaryConditionFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02d/config.jl b/example/1d-linear-convection/weno3/julia/02d/config.jl new file mode 100644 index 00000000..bf48de3a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02d/config.jl @@ -0,0 +1,68 @@ +# julia/config.jl +""" +CfdConfig:与 Python config.py 完全同构 +""" +mutable struct CfdConfig + ic_type::String + recon_scheme::String + flux_type::String + rk_order::Int + wave_speed::Float64 + final_time::Float64 + dt::Float64 + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + spatial_order::Int + + function CfdConfig() + new( + "step", + "eno", + "rusanov", + 1, + 1.0, + 0.625, + 0.025, + "periodic", + 1.0, + 2.0, + 2 + ) + end +end + +""" +专用配置:重建方案(链式调用) +""" +function with_reconstruction(cfg::CfdConfig, scheme::String, order::Union{Int, Nothing}=nothing) + cfg.recon_scheme = lowercase(scheme) + + if order !== nothing + cfg.spatial_order = order + else + if startswith(cfg.recon_scheme, "weno") + cfg.spatial_order = 5 + elseif cfg.recon_scheme == "eno" + cfg.spatial_order = 3 + else + error("不支持的重建格式:$scheme(仅支持 eno/weno)") + end + end + + return cfg # 支持链式调用 +end + +""" +专用配置:边界条件(链式调用) +""" +function with_boundary(cfg::CfdConfig, bc_type::String; left_value=nothing, right_value=nothing) + cfg.boundary_type = bc_type + if left_value !== nothing + cfg.left_boundary_value = left_value + end + if right_value !== nothing + cfg.right_boundary_value = right_value + end + return cfg +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02d/domain.jl b/example/1d-linear-convection/weno3/julia/02d/domain.jl new file mode 100644 index 00000000..a7edc226 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02d/domain.jl @@ -0,0 +1,61 @@ +# julia/domain.jl +""" +Domain 结构体(与 domain.py 完全同构) +- 保存 config +- nghosts 计算逻辑 1:1 +- ist/ied 为 0-based(与 Python 一致) +""" + +include("mesh.jl") + +mutable struct Domain + config::Any # 保存 config(与 Python 一致) + mesh::Mesh + nghosts::Int + ist::Int # 0-based + ied::Int # 0-based + ntcells::Int +end + +function Domain(config::Any, mesh::Mesh) + nghosts = _calc_nghosts(config) + ist = nghosts + 1 # ← 1-based 起始索引 + ied = ist + mesh.ncells # ← 1-based 结束索引(不包含) + ntcells = mesh.ncells + 2 * nghosts + Domain(config, mesh, nghosts, ist, ied, ntcells) +end + +function _calc_nghosts(config::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + order = getfield_safe(config, :spatial_order, 2) + + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + if scheme == "eno" + nghosts = order + elseif startswith(scheme, "weno") + nghosts = order ÷ 2 + 1 + else + error("未知重建格式 $(scheme),无法计算ghost层!") + end + + if nghosts <= 0 + error("计算得到的ghost层数量无效:$(nghosts)(阶数$(order),格式$(scheme))") + end + + return nghosts +end + +function is_physical_cell(domain::Domain, idx::Int) + return domain.ist <= idx < domain.ied +end + +function get_physical_indices(domain::Domain) + return domain.ist:(domain.ied - 1) +end + +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02d/flux.jl b/example/1d-linear-convection/weno3/julia/02d/flux.jl new file mode 100644 index 00000000..cff23582 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02d/flux.jl @@ -0,0 +1,112 @@ +# julia/flux.jl +""" +通量计算器模块(与 flux.py 完全同构) +- 抽象基类 + 具体实现 +- 字段:cfd, config, mesh, wave_speed +""" + +include("mesh.jl") + +# ---------------------- 抽象基类 ---------------------- +""" +InviscidFluxCalculator 抽象类型 +Julia 无 ABC,用文档约定 +所有子类型必须实现 compute! +""" +abstract type InviscidFluxCalculator end + +# ---------------------- RusanovFluxCalculator ---------------------- +mutable struct RusanovFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function RusanovFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::RusanovFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = calc.wave_speed + c_R = calc.wave_speed + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + end +end + +# ---------------------- EngquistOsherFluxCalculator ---------------------- +mutable struct EngquistOsherFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function EngquistOsherFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::EngquistOsherFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + c = calc.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + end +end + +# ---------------------- FluxCalculatorFactory ---------------------- +module FluxCalculatorFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "通量计算器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + config = cfd.config + if !hasproperty(config, :flux_type) + error("cfd.config 缺少 flux_type 字段") + end + + flux_type = config.flux_type + if !(flux_type isa AbstractString) + error("flux_type 必须为字符串,当前值: $flux_type") + end + + if !haskey(_REGISTRY, flux_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的通量计算器: '$flux_type'。可用选项: $available") + end + + return _REGISTRY[flux_type](cfd) +end + +# ✅ 修正:使用 Main. 前缀 +register("rusanov", cfd -> Main.RusanovFluxCalculator(cfd)) +register("engquist-osher", cfd -> Main.EngquistOsherFluxCalculator(cfd)) + +end # module FluxCalculatorFactory + +export FluxCalculatorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02d/initial_condition.jl b/example/1d-linear-convection/weno3/julia/02d/initial_condition.jl new file mode 100644 index 00000000..5e029e8d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02d/initial_condition.jl @@ -0,0 +1,86 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02d/mesh.jl b/example/1d-linear-convection/weno3/julia/02d/mesh.jl new file mode 100644 index 00000000..1b0dd4bf --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02d/mesh.jl @@ -0,0 +1,45 @@ +# julia/mesh.jl +""" +Mesh 结构体(与提供的 mesh.py 完全同构) +- 字段名、初始化顺序、循环逻辑完全一致 +- 不做任何泛化或优化 +""" + +mutable struct Mesh + xmin::Float64 + xmax::Float64 + ncells::Int + nnodes::Int + nx::Int + x::Vector{Float64} + xcc::Vector{Float64} + L::Float64 # 在 init_mesh 中赋值 + dx::Float64 # 在 init_mesh 中赋值 + + function Mesh() + # 与 Python __init__ 完全一致 + xmin = 0.0 + xmax = 2.0 + ncells = 40 + nnodes = ncells + 1 + nx = ncells + x = zeros(Float64, nnodes) + xcc = zeros(Float64, ncells) + + # 调用 init_mesh(模拟 Python) + L = xmax - xmin + dx = L / ncells + + # Generate node coordinates: for i in range(self.nnodes) + for i in 1:nnodes + x[i] = xmin + (i - 1) * dx # Python i → Julia i-1 + end + + # Generate cell center coordinates + for i in 1:ncells + xcc[i] = 0.5 * (x[i] + x[i+1]) + end + + new(xmin, xmax, ncells, nnodes, nx, x, xcc, L, dx) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02d/plotter.jl b/example/1d-linear-convection/weno3/julia/02d/plotter.jl new file mode 100644 index 00000000..a77d8d68 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02d/plotter.jl @@ -0,0 +1,147 @@ +# julia/plotter.jl +""" +CFDPlotter 的 Julia 实现(通过 PythonCall.jl 调用 Matplotlib) +确保与 Python plotter.py 行为完全一致 +""" + +using PythonCall + +# 初始化 Python 环境(加载 matplotlib, inflect) +const plt = pyimport("matplotlib.pyplot") +const inflect = pyimport("inflect") + +mutable struct CFDPlotter + default_styles::Dict{String, Any} + p::Py +end + +function CFDPlotter() + default_styles = Dict{String, Any}( + "numerical" => Dict( + :color => "blue", + :linestyle => "-", + :marker => "o", + :markerfacecolor => "none" + ), + "analytical" => Dict( + :color => "red", + :linestyle => "--", + :marker => "", + :linewidth => 1.5 + ), + "comparison" => [ + Dict(:color => "black", :linestyle => "-", :marker => "o", :markerfacecolor => "none"), + Dict(:color => "blue", :linestyle => "--", :marker => "s", :markerfacecolor => "none"), + Dict(:color => "green", :linestyle => ":", :marker => "^", :markerfacecolor => "none") + ] + ) + p = inflect.engine() + CFDPlotter(default_styles, p) +end + +""" +轻量即时绘图(快速验证结果) +""" +function plot_quick(plotter::CFDPlotter, cfd_result::Dict; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + rk_order = cfd_result["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = cfd_result["config"]["final_time"] + order = cfd_result["config"]["order"] + scheme = uppercase(cfd_result["config"]["scheme"]) + actual_title = "1D Convection (t=$(final_time))\n$(order)th-order $(scheme) + $(rk_str)-order RK" + end + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"]; + label="Numerical ($(uppercase(cfd_result["config"]["scheme"])))", + plotter.default_styles["numerical"]..., + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + # 通用样式 + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +""" +多格式/多精度对比绘图 +""" +function plot_comparison(plotter::CFDPlotter, result_list::Vector{Dict{String, Any}}; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + schemes = [uppercase(r["config"]["scheme"]) * string(r["config"]["order"]) for r in result_list] + rk_order = result_list[1]["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = result_list[1]["config"]["final_time"] + actual_title = "1D Convection Comparison (t=$(final_time))\n$(join(schemes, ", ")) + $(rk_str)-order RK" + end + + # 绘制多个数值解 + for (i, res) in enumerate(result_list) + style = plotter.default_styles["comparison"][mod1(i, length(plotter.default_styles["comparison"]))] + label = "Numerical ($(uppercase(res["config"]["scheme"]))$(res["config"]["order"]))" + plt.plot( + res["x"], res["numerical"]; + label=label, + style..., + markersize=5, linewidth=0.5 + ) + end + + # 绘制解析解 + plt.plot( + result_list[1]["x"], result_list[1]["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +function _set_common_style(plotter::CFDPlotter, title::String) + plt.title(title, fontsize=12) + plt.xlabel("x", fontsize=10) + plt.ylabel("u", fontsize=10) + plt.legend(fontsize=9) + plt.grid(true, color="gray", linestyle="--", linewidth=0.5, alpha=0.7) + plt.tight_layout() +end + +""" +快捷函数:ENO/WENO对比绘图 +""" +function plot_eno_weno_comparison(eno_result::Dict, weno_result::Dict; save_path=nothing) + plotter = CFDPlotter() + plot_comparison(plotter, [eno_result, weno_result]; save_path=save_path) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02d/reconstructor/eno.jl b/example/1d-linear-convection/weno3/julia/02d/reconstructor/eno.jl new file mode 100644 index 00000000..e78a636f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02d/reconstructor/eno.jl @@ -0,0 +1,107 @@ +# julia/reconstructor/eno.jl +""" +ENO 重构器(与 reconstructor/eno.py 完全同构) +""" + +# ---------------------- ENO 系数初始化 ---------------------- +function _init_eno_coef!(spatial_order::Int, coef::Matrix{Float64}) + if spatial_order == 1 + coef[1, 1] = 1.0 + coef[2, 1] = 1.0 + elseif spatial_order == 2 + coef[1, 1:2] = [3.0/2.0, -1.0/2.0] + coef[2, 1:2] = [1.0/2.0, 1.0/2.0] + coef[3, 1:2] = [-1.0/2.0, 3.0/2.0] + elseif spatial_order == 3 + coef[1, 1:3] = [11.0/6.0, -7.0/6.0, 1.0/3.0] + coef[2, 1:3] = [1.0/3.0, 5.0/6.0, -1.0/6.0] + coef[3, 1:3] = [-1.0/6.0, 5.0/6.0, 1.0/3.0] + coef[4, 1:3] = [1.0/3.0, -7.0/6.0, 11.0/6.0] + elseif spatial_order == 4 + coef[1, 1:4] = [25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0] + coef[2, 1:4] = [1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0] + coef[3, 1:4] = [-1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0] + coef[4, 1:4] = [1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0] + coef[5, 1:4] = [-1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0] + elseif spatial_order == 5 + coef[1, 1:5] = [137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0] + coef[2, 1:5] = [1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0] + coef[3, 1:5] = [-1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0] + coef[4, 1:5] = [1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0] + coef[5, 1:5] = [-1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0] + coef[6, 1:5] = [1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0] + elseif spatial_order == 6 + coef[1, 1:6] = [49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0] + coef[2, 1:6] = [1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0] + coef[3, 1:6] = [-1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0] + coef[4, 1:6] = [1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0] + coef[5, 1:6] = [-1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0] + coef[6, 1:6] = [1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0] + coef[7, 1:6] = [-1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0] + elseif spatial_order == 7 + coef[1, 1:7] = [363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0] + coef[2, 1:7] = [1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0] + coef[3, 1:7] = [-1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0] + coef[4, 1:7] = [1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0] + coef[5, 1:7] = [-1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0] + coef[6, 1:7] = [1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0] + coef[7, 1:7] = [-1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0] + coef[8, 1:7] = [1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0] + else + error("ENO 系数未实现 order=$spatial_order") + end +end + +# ---------------------- ENO 重构器 ---------------------- +mutable struct EnoReconstructor + spatial_order::Int + ntcells::Int + lmc::Vector{Int} + coef::Matrix{Float64} + dd::Matrix{Float64} + + function EnoReconstructor(spatial_order::Int, ntcells::Int) + lmc = zeros(Int, ntcells) + coef = zeros(Float64, spatial_order + 1, spatial_order) + dd = zeros(Float64, spatial_order, ntcells) + _init_eno_coef!(spatial_order, coef) + new(spatial_order, ntcells, lmc, coef, dd) + end +end + +function reconstruct(rec::EnoReconstructor, q::Vector{Float64}, cfd::Any) + # 1. 差商计算 (dd[1,:] = q) + @views rec.dd[1, :] .= q + for m in 2:rec.spatial_order + for j in 1:(rec.ntcells - m + 1) + rec.dd[m, j] = rec.dd[m-1, j+1] - rec.dd[m-1, j] + end + end + + # 2. 选择 smoothest stencil + domain = cfd.domain + for i in (domain.ist - 1):(domain.ied) # Python: range(ist-1, ied+1) → ied+1-1 = ied + rec.lmc[i] = i + for m in 2:rec.spatial_order + if abs(rec.dd[m, rec.lmc[i] - 1]) < abs(rec.dd[m, rec.lmc[i]]) + rec.lmc[i] -= 1 + end + end + end + + # 3. 重构界面值 + solution = cfd.solution + for i in domain.ist:(domain.ied) # Python: range(ist, ied+1) → ied+1-1 = ied + j = i - domain.ist + 1 # Julia 1-based + k1 = rec.lmc[i - 1] + k2 = rec.lmc[i] + r1 = (i - 1) - k1 + 1 + r2 = i - k2 + 1 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in 1:rec.spatial_order + solution.q_face_left[j] += q[k1 + m - 1] * rec.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m - 1] * rec.coef[r2, m] + end + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02d/reconstructor/weno3.jl b/example/1d-linear-convection/weno3/julia/02d/reconstructor/weno3.jl new file mode 100644 index 00000000..2b6fe1ab --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02d/reconstructor/weno3.jl @@ -0,0 +1,65 @@ +# julia/reconstructor/weno3.jl +""" +WENO3 重构器(与 reconstructor/weno3.py 完全同构) +""" + +mutable struct Weno3Reconstructor + # 无字段,与 Python 一致 +end + +function reconstruct(rec::Weno3Reconstructor, q::Vector{Float64}, cfd::Any) + domain = cfd.domain + solution = cfd.solution + _reconstruct_left_interfaces(domain, q, solution.q_face_left) + _reconstruct_right_interfaces(domain, q, solution.q_face_right) +end + +function _reconstruct_left_interfaces(domain, u, qL) + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in (domain.ist - 1):(domain.ied - 1) + j = i - (domain.ist - 1) + 1 # ← Julia 1-based: j = i - (ist-1) 对应 Python j = i - (ist-1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = _reconstruct_from_left_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_right_interfaces(domain, u, qR) + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in domain.ist:domain.ied + j = i - domain.ist + 1 # ← Julia 1-based: j = i - ist 对应 Python j = i - ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = _reconstruct_from_right_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_from_left_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -0.5*v1 + 1.5*v2 # r=1 stencil + q1 = 0.5*v2 + 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end + +function _reconstruct_from_right_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 0.5*v1 + 0.5*v2 # r=1 stencil + q1 = 1.5*v2 - 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02d/residual.jl b/example/1d-linear-convection/weno3/julia/02d/residual.jl new file mode 100644 index 00000000..e8fd6584 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02d/residual.jl @@ -0,0 +1,65 @@ +# julia/residual.jl +""" +残差计算器(与 residual.py 完全同构) +- 封装重建→通量→散度完整流程 +- 依赖 cfd 的多个字段 +""" + +include("mesh.jl") + +mutable struct ResidualCalculator + cfd::Any + config::Any + domain::Any + solution::Any + mesh::Mesh + reconstructor::Any + flux_calculator::Any # 通量计算器(外部传入,替代工厂) + + function ResidualCalculator(cfd::Any, flux_calculator::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + mesh = domain.mesh + reconstructor = cfd.reconstructor + + new(cfd, config, domain, solution, mesh, reconstructor, flux_calculator) + end +end + +""" +计算完整残差(对外唯一接口) +""" +function compute!(calc::ResidualCalculator) + _reconstruct(calc) + _compute_inviscid_flux(calc) + _compute_flux_divergence(calc) +end + +""" +私有方法:界面值重建 +""" +function _reconstruct(calc::ResidualCalculator) + reconstruct(calc.reconstructor, calc.solution.u, calc.cfd) +end + +""" +私有方法:计算无粘通量 +""" +function _compute_inviscid_flux(calc::ResidualCalculator) + compute!(calc.flux_calculator, + calc.solution.q_face_left, + calc.solution.q_face_right, + calc.solution.flux) +end + +""" +私有方法:计算通量散度(残差 = -dF/dx) +""" +function _compute_flux_divergence(calc::ResidualCalculator) + solution = calc.solution + mesh = calc.mesh + for i in 1:mesh.ncells + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / mesh.dx + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02d/run_eno_weno.jl b/example/1d-linear-convection/weno3/julia/02d/run_eno_weno.jl new file mode 100644 index 00000000..58b6cfa5 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02d/run_eno_weno.jl @@ -0,0 +1,50 @@ +# julia/run_eno_weno.jl +""" +1:1 复刻 run_eno_weno.py 的 Julia 版本 +""" + +include("config.jl") +include("mesh.jl") +include("solver.jl") +include("plotter.jl") + +function performEnoWenoAnalysis() + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + println("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + with_reconstruction(config_eno3, "eno", 3) # 显式指定 3 阶 + config_eno3.dt = 0.0025 # 覆盖默认值 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + run!(cfd_eno3) # 求解并生成 result 字典 + + # 3. 配置并运行 WENO3 求解 + println("Running WENO3 solver...") + config_weno3 = CfdConfig() + with_reconstruction(config_weno3, "weno", 3) # 显式指定 3 阶(WENO 默认 5 阶) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + run!(cfd_weno3) + + # 5. 绘制 ENO/WENO 对比图 + println("Plotting comparison results...") + plot_eno_weno_comparison( + cfd_eno3.result, + cfd_weno3.result; + save_path="eno_weno_comparison.png" + ) + + return cfd_eno3, cfd_weno3 +end + +# 主程序入口 +if abspath(PROGRAM_FILE) == @__FILE__ + performEnoWenoAnalysis() + println("Analysis completed!") +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02d/solution.jl b/example/1d-linear-convection/weno3/julia/02d/solution.jl new file mode 100644 index 00000000..90ca0393 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02d/solution.jl @@ -0,0 +1,75 @@ +# julia/solution.jl +""" +Solution 结构体(与真实 solution.py 完全同构) +字段顺序、方法、逻辑 1:1 对齐 +""" + +include("mesh.jl") +include("domain.jl") +include("initial_condition.jl") + +mutable struct Solution + domain::Domain + q_face_left::Vector{Float64} + q_face_right::Vector{Float64} + flux::Vector{Float64} + res::Vector{Float64} + u::Vector{Float64} + un::Vector{Float64} + + function Solution(config::Any, domain::Domain) + mesh = domain.mesh + + q_face_left = zeros(Float64, mesh.nnodes) + q_face_right = zeros(Float64, mesh.nnodes) + flux = zeros(Float64, mesh.nnodes) + res = zeros(Float64, mesh.ncells) + u = zeros(Float64, domain.ntcells) + un = zeros(Float64, domain.ntcells) + + sol = new(domain, q_face_left, q_face_right, flux, res, u, un) + initialize_from_config(sol, config) + return sol + end +end + +""" +重置解数组为初始状态 +""" +function reset_solution(sol::Solution) + fill!(sol.u, 0.0) + fill!(sol.un, 0.0) +end + +""" +根据配置初始化场 +注:暂用硬编码替代 InitialConditionFactory +""" +function initialize_from_config(sol::Solution, config::Any) + ic_type = getfield_safe(config, :ic_type, "step") + + # 硬编码创建 IC(替代 InitialConditionFactory) + ic = if ic_type == "step" + StepFunctionIC(config) + elseif ic_type == "sin" + SineWaveIC(config) + elseif ic_type == "gaussian" + GaussianPulseIC(config) + else + error("未知初始条件类型: $ic_type") + end + + apply(ic, sol) # 调用 IC.apply +end + +""" +更新旧场:un = u +""" +function update_old_field(sol::Solution) + sol.un .= sol.u # 等价于 Python 的 un[:] = u[:] +end + +# ---------------------- 辅助函数 ---------------------- +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02d/solver.jl b/example/1d-linear-convection/weno3/julia/02d/solver.jl new file mode 100644 index 00000000..ea015f6e --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02d/solver.jl @@ -0,0 +1,167 @@ +# julia/solver.jl +""" +CFD 求解器主类(与 solver.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("initial_condition.jl") +include("boundary.jl") +include("flux.jl") +include("residual.jl") +include("time_integration.jl") +include("reconstructor/eno.jl") +include("reconstructor/weno3.jl") + +# 导入工厂模块(必须在顶层!) +using .FluxCalculatorFactory +using .TimeIntegratorFactory +using .BoundaryConditionFactory + +# ---------------------- Cfd 求解器 ---------------------- +mutable struct Cfd + config::Any + domain::Domain + solution::Solution + reconstructor::Any + residual_calculator::ResidualCalculator + integrator::Any + boundary_condition::Any + result::Dict{String, Any} + + function Cfd(config::Any, mesh::Mesh) + domain = Domain(config, mesh) + solution = Solution(config, domain) + + # 在 Cfd 构造函数中 + # =============== 重建器创建 =============== + recon_scheme = config.recon_scheme + spatial_order = config.spatial_order + + # ✅ 模拟 Python ReconstructorFactory 的逻辑 + if recon_scheme == "weno" + recon_scheme = "weno$(spatial_order)" + end + + # 现在根据 recon_scheme 创建 + reconstructor = if recon_scheme == "eno" + EnoReconstructor(spatial_order, domain.ntcells) + elseif recon_scheme == "weno3" + Weno3Reconstructor() + else + error("不支持的重建格式: $recon_scheme") + end + + # 构造 cfd 上下文(NamedTuple) + cfd_context = ( + config = config, + domain = domain + ) + + # 创建通量计算器 + flux_calculator = FluxCalculatorFactory.create(cfd_context) + + # 残差计算器 + residual_calculator = ResidualCalculator( + (config=config, domain=domain, solution=solution, reconstructor=reconstructor), + flux_calculator + ) + + # 构建边界条件上下文 + bc_context = (config = config, domain = domain) + + # 使用工厂创建边界条件 + boundary_condition = BoundaryConditionFactory.create(bc_context) + + # 构建时间推进器所需上下文 + integrator_context = ( + config = config, + domain = domain, + solution = solution, + residual_calculator = residual_calculator, + boundary_condition = boundary_condition + ) + + # 使用工厂创建时间推进器 + integrator = TimeIntegratorFactory.create(integrator_context) + #@show typeof(integrator) + + # 注入 cfd 到 residual_calculator 和 integrator + residual_calculator.cfd = (config=config, domain=domain, solution=solution, reconstructor=reconstructor, residual_calculator=residual_calculator, integrator=integrator, boundary_condition=boundary_condition) + integrator.base.cfd = residual_calculator.cfd + + result = Dict{String, Any}() + new(config, domain, solution, reconstructor, residual_calculator, integrator, boundary_condition, result) + end +end + +""" +通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界 +""" +function exact_solution(cfd::Cfd) + x = cfd.domain.mesh.xcc + T = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = @. (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = if cfd.config.ic_type == "step" + StepFunctionIC(cfd.config) + elseif cfd.config.ic_type == "sin" + SineWaveIC(cfd.config) + elseif cfd.config.ic_type == "gaussian" + GaussianPulseIC(cfd.config) + else + error("未知初始条件: $(cfd.config.ic_type)") + end + + return evaluate_at(ic, x_shifted) +end + +""" +主求解循环 +""" +function run!(cfd::Cfd) + # 应用初始边界条件并同步 old field + apply!(cfd.boundary_condition, cfd.solution.u) + update_old_field(cfd.solution) + + t = 0.0 + dt_old = cfd.config.dt + dt = dt_old + + while t < cfd.config.final_time + if t + dt > cfd.config.final_time + dt = cfd.config.final_time - t + end + #@show t, dt, maximum(cfd.solution.u), minimum(cfd.solution.u) + # 执行时间步 + step(cfd.integrator, dt) + t += dt + end + + # 恢复 dt + cfd.config.dt = dt_old + + # 整理结果 + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied-1] # Python: [ist:ied] + analytical = exact_solution(cfd) + + cfd.result = Dict( + "x" => cfd.domain.mesh.xcc, + "numerical" => u_numerical, + "analytical" => analytical, + "config" => Dict( + "scheme" => cfd.config.recon_scheme, + "order" => cfd.config.spatial_order, + "rk_order" => cfd.config.rk_order, + "final_time" => cfd.config.final_time + ) + ) + + return u_numerical +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02d/time_integration.jl b/example/1d-linear-convection/weno3/julia/02d/time_integration.jl new file mode 100644 index 00000000..add564ba --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02d/time_integration.jl @@ -0,0 +1,169 @@ +# julia/time_integration.jl +""" +时间推进器模块(与 time_integration.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("residual.jl") +include("boundary.jl") + +# ---------------------- 抽象时间推进器基类 ---------------------- +abstract type TimeIntegrator end + +mutable struct TimeIntegratorBase <: TimeIntegrator + cfd::Any + config::Any + domain::Domain + solution::Solution + residual_calculator::Any # ResidualCalculator +end + +function TimeIntegratorBase(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + residual_calculator = cfd.residual_calculator + TimeIntegratorBase(cfd, config, domain, solution, residual_calculator) +end + +function compute_residual(integrator::TimeIntegratorBase) + compute!(integrator.residual_calculator) +end + +function apply_boundary(integrator::TimeIntegratorBase) + apply!(integrator.cfd.boundary_condition, integrator.solution.u) +end + +function map_idx(integrator::TimeIntegratorBase, i::Int) + return i - integrator.domain.ist + 1 # ← +1 转为 1-based +end + +# ---------------------- RK1Integrator ---------------------- +mutable struct RK1Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK1Integrator(cfd::Any) + RK1Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK1Integrator, dt::Float64) + compute_residual(integrator.base) + for i in integrator.base.domain.ist:(integrator.base.domain.ied - 1) + j = map_idx(integrator.base, i) + integrator.base.solution.u[i] += dt * integrator.base.solution.res[j] + end + apply_boundary(integrator.base) + update_old_field(integrator.base.solution) +end + +# ---------------------- RK2Integrator ---------------------- +mutable struct RK2Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK2Integrator(cfd::Any) + RK2Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK2Integrator, dt::Float64) + base = integrator.base + # 阶段1:预测步 + compute_residual(base) + u_pred = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u_pred[i] += dt * base.solution.res[j] + end + base.solution.u .= u_pred + apply_boundary(base) + # 阶段2:校正步 + compute_residual(base) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = 0.5 * base.solution.un[i] + 0.5 * base.solution.u[i] + 0.5 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- RK3Integrator ---------------------- +mutable struct RK3Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK3Integrator(cfd::Any) + RK3Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK3Integrator, dt::Float64) + base = integrator.base + # 阶段1 + compute_residual(base) + u1 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u1[i] += dt * base.solution.res[j] + end + base.solution.u .= u1 + apply_boundary(base) + # 阶段2 + compute_residual(base) + u2 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u2[i] = 0.75 * base.solution.un[i] + 0.25 * base.solution.u[i] + 0.25 * dt * base.solution.res[j] + end + base.solution.u .= u2 + apply_boundary(base) + # 阶段3 + compute_residual(base) + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = c1 * base.solution.un[i] + c2 * base.solution.u[i] + c3 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- TimeIntegratorFactory ---------------------- +module TimeIntegratorFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "时间积分器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + rk_order = cfd.config.rk_order + if !(rk_order isa Integer) || rk_order < 1 + error("rk_order 必须为正整数,当前值: $rk_order") + end + + name = "rk$rk_order" + if !haskey(_REGISTRY, name) + available = sort(collect(keys(_REGISTRY))) + error("未注册的时间积分器: '$name'。可用选项: $available") + end + + return _REGISTRY[name](cfd) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("rk1", cfd -> Main.RK1Integrator(cfd)) +register("rk2", cfd -> Main.RK2Integrator(cfd)) +register("rk3", cfd -> Main.RK3Integrator(cfd)) + +end # module TimeIntegratorFactory + +export TimeIntegratorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02e/boundary.jl b/example/1d-linear-convection/weno3/julia/02e/boundary.jl new file mode 100644 index 00000000..396c1264 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02e/boundary.jl @@ -0,0 +1,129 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end + + +# ---------------------- BoundaryConditionFactory ---------------------- +module BoundaryConditionFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "边界条件 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + bc_type = cfd.config.boundary_type + if !(bc_type isa AbstractString) + error("boundary_type 必须为字符串,当前值: $bc_type") + end + + if !haskey(_REGISTRY, bc_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的边界条件: '$bc_type'。可用选项: $available") + end + + return _REGISTRY[bc_type](cfd) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("periodic", cfd -> Main.PeriodicBoundary(cfd)) +register("dirichlet", cfd -> Main.DirichletBoundary(cfd)) +register("neumann", cfd -> Main.NeumannBoundary(cfd)) + +end # module BoundaryConditionFactory + +export BoundaryConditionFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02e/config.jl b/example/1d-linear-convection/weno3/julia/02e/config.jl new file mode 100644 index 00000000..bf48de3a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02e/config.jl @@ -0,0 +1,68 @@ +# julia/config.jl +""" +CfdConfig:与 Python config.py 完全同构 +""" +mutable struct CfdConfig + ic_type::String + recon_scheme::String + flux_type::String + rk_order::Int + wave_speed::Float64 + final_time::Float64 + dt::Float64 + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + spatial_order::Int + + function CfdConfig() + new( + "step", + "eno", + "rusanov", + 1, + 1.0, + 0.625, + 0.025, + "periodic", + 1.0, + 2.0, + 2 + ) + end +end + +""" +专用配置:重建方案(链式调用) +""" +function with_reconstruction(cfg::CfdConfig, scheme::String, order::Union{Int, Nothing}=nothing) + cfg.recon_scheme = lowercase(scheme) + + if order !== nothing + cfg.spatial_order = order + else + if startswith(cfg.recon_scheme, "weno") + cfg.spatial_order = 5 + elseif cfg.recon_scheme == "eno" + cfg.spatial_order = 3 + else + error("不支持的重建格式:$scheme(仅支持 eno/weno)") + end + end + + return cfg # 支持链式调用 +end + +""" +专用配置:边界条件(链式调用) +""" +function with_boundary(cfg::CfdConfig, bc_type::String; left_value=nothing, right_value=nothing) + cfg.boundary_type = bc_type + if left_value !== nothing + cfg.left_boundary_value = left_value + end + if right_value !== nothing + cfg.right_boundary_value = right_value + end + return cfg +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02e/domain.jl b/example/1d-linear-convection/weno3/julia/02e/domain.jl new file mode 100644 index 00000000..a7edc226 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02e/domain.jl @@ -0,0 +1,61 @@ +# julia/domain.jl +""" +Domain 结构体(与 domain.py 完全同构) +- 保存 config +- nghosts 计算逻辑 1:1 +- ist/ied 为 0-based(与 Python 一致) +""" + +include("mesh.jl") + +mutable struct Domain + config::Any # 保存 config(与 Python 一致) + mesh::Mesh + nghosts::Int + ist::Int # 0-based + ied::Int # 0-based + ntcells::Int +end + +function Domain(config::Any, mesh::Mesh) + nghosts = _calc_nghosts(config) + ist = nghosts + 1 # ← 1-based 起始索引 + ied = ist + mesh.ncells # ← 1-based 结束索引(不包含) + ntcells = mesh.ncells + 2 * nghosts + Domain(config, mesh, nghosts, ist, ied, ntcells) +end + +function _calc_nghosts(config::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + order = getfield_safe(config, :spatial_order, 2) + + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + if scheme == "eno" + nghosts = order + elseif startswith(scheme, "weno") + nghosts = order ÷ 2 + 1 + else + error("未知重建格式 $(scheme),无法计算ghost层!") + end + + if nghosts <= 0 + error("计算得到的ghost层数量无效:$(nghosts)(阶数$(order),格式$(scheme))") + end + + return nghosts +end + +function is_physical_cell(domain::Domain, idx::Int) + return domain.ist <= idx < domain.ied +end + +function get_physical_indices(domain::Domain) + return domain.ist:(domain.ied - 1) +end + +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02e/flux.jl b/example/1d-linear-convection/weno3/julia/02e/flux.jl new file mode 100644 index 00000000..cff23582 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02e/flux.jl @@ -0,0 +1,112 @@ +# julia/flux.jl +""" +通量计算器模块(与 flux.py 完全同构) +- 抽象基类 + 具体实现 +- 字段:cfd, config, mesh, wave_speed +""" + +include("mesh.jl") + +# ---------------------- 抽象基类 ---------------------- +""" +InviscidFluxCalculator 抽象类型 +Julia 无 ABC,用文档约定 +所有子类型必须实现 compute! +""" +abstract type InviscidFluxCalculator end + +# ---------------------- RusanovFluxCalculator ---------------------- +mutable struct RusanovFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function RusanovFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::RusanovFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = calc.wave_speed + c_R = calc.wave_speed + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + end +end + +# ---------------------- EngquistOsherFluxCalculator ---------------------- +mutable struct EngquistOsherFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function EngquistOsherFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::EngquistOsherFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + c = calc.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + end +end + +# ---------------------- FluxCalculatorFactory ---------------------- +module FluxCalculatorFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "通量计算器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + config = cfd.config + if !hasproperty(config, :flux_type) + error("cfd.config 缺少 flux_type 字段") + end + + flux_type = config.flux_type + if !(flux_type isa AbstractString) + error("flux_type 必须为字符串,当前值: $flux_type") + end + + if !haskey(_REGISTRY, flux_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的通量计算器: '$flux_type'。可用选项: $available") + end + + return _REGISTRY[flux_type](cfd) +end + +# ✅ 修正:使用 Main. 前缀 +register("rusanov", cfd -> Main.RusanovFluxCalculator(cfd)) +register("engquist-osher", cfd -> Main.EngquistOsherFluxCalculator(cfd)) + +end # module FluxCalculatorFactory + +export FluxCalculatorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02e/initial_condition.jl b/example/1d-linear-convection/weno3/julia/02e/initial_condition.jl new file mode 100644 index 00000000..aa120160 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02e/initial_condition.jl @@ -0,0 +1,120 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end + +# ---------------------- InitialConditionFactory ---------------------- +module InitialConditionFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "初始条件 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(ic_type::String, config::Any) + if !(ic_type isa AbstractString) + error("ic_type 必须为字符串,当前值: $ic_type") + end + + if !haskey(_REGISTRY, ic_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的初始条件: '$ic_type'。可用选项: $available") + end + + return _REGISTRY[ic_type](config) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("step", config -> Main.StepFunctionIC(config)) +register("sin", config -> Main.SineWaveIC(config)) +register("gaussian", config -> Main.GaussianPulseIC(config)) + +end # module InitialConditionFactory + +export InitialConditionFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02e/mesh.jl b/example/1d-linear-convection/weno3/julia/02e/mesh.jl new file mode 100644 index 00000000..1b0dd4bf --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02e/mesh.jl @@ -0,0 +1,45 @@ +# julia/mesh.jl +""" +Mesh 结构体(与提供的 mesh.py 完全同构) +- 字段名、初始化顺序、循环逻辑完全一致 +- 不做任何泛化或优化 +""" + +mutable struct Mesh + xmin::Float64 + xmax::Float64 + ncells::Int + nnodes::Int + nx::Int + x::Vector{Float64} + xcc::Vector{Float64} + L::Float64 # 在 init_mesh 中赋值 + dx::Float64 # 在 init_mesh 中赋值 + + function Mesh() + # 与 Python __init__ 完全一致 + xmin = 0.0 + xmax = 2.0 + ncells = 40 + nnodes = ncells + 1 + nx = ncells + x = zeros(Float64, nnodes) + xcc = zeros(Float64, ncells) + + # 调用 init_mesh(模拟 Python) + L = xmax - xmin + dx = L / ncells + + # Generate node coordinates: for i in range(self.nnodes) + for i in 1:nnodes + x[i] = xmin + (i - 1) * dx # Python i → Julia i-1 + end + + # Generate cell center coordinates + for i in 1:ncells + xcc[i] = 0.5 * (x[i] + x[i+1]) + end + + new(xmin, xmax, ncells, nnodes, nx, x, xcc, L, dx) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02e/plotter.jl b/example/1d-linear-convection/weno3/julia/02e/plotter.jl new file mode 100644 index 00000000..a77d8d68 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02e/plotter.jl @@ -0,0 +1,147 @@ +# julia/plotter.jl +""" +CFDPlotter 的 Julia 实现(通过 PythonCall.jl 调用 Matplotlib) +确保与 Python plotter.py 行为完全一致 +""" + +using PythonCall + +# 初始化 Python 环境(加载 matplotlib, inflect) +const plt = pyimport("matplotlib.pyplot") +const inflect = pyimport("inflect") + +mutable struct CFDPlotter + default_styles::Dict{String, Any} + p::Py +end + +function CFDPlotter() + default_styles = Dict{String, Any}( + "numerical" => Dict( + :color => "blue", + :linestyle => "-", + :marker => "o", + :markerfacecolor => "none" + ), + "analytical" => Dict( + :color => "red", + :linestyle => "--", + :marker => "", + :linewidth => 1.5 + ), + "comparison" => [ + Dict(:color => "black", :linestyle => "-", :marker => "o", :markerfacecolor => "none"), + Dict(:color => "blue", :linestyle => "--", :marker => "s", :markerfacecolor => "none"), + Dict(:color => "green", :linestyle => ":", :marker => "^", :markerfacecolor => "none") + ] + ) + p = inflect.engine() + CFDPlotter(default_styles, p) +end + +""" +轻量即时绘图(快速验证结果) +""" +function plot_quick(plotter::CFDPlotter, cfd_result::Dict; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + rk_order = cfd_result["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = cfd_result["config"]["final_time"] + order = cfd_result["config"]["order"] + scheme = uppercase(cfd_result["config"]["scheme"]) + actual_title = "1D Convection (t=$(final_time))\n$(order)th-order $(scheme) + $(rk_str)-order RK" + end + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"]; + label="Numerical ($(uppercase(cfd_result["config"]["scheme"])))", + plotter.default_styles["numerical"]..., + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + # 通用样式 + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +""" +多格式/多精度对比绘图 +""" +function plot_comparison(plotter::CFDPlotter, result_list::Vector{Dict{String, Any}}; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + schemes = [uppercase(r["config"]["scheme"]) * string(r["config"]["order"]) for r in result_list] + rk_order = result_list[1]["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = result_list[1]["config"]["final_time"] + actual_title = "1D Convection Comparison (t=$(final_time))\n$(join(schemes, ", ")) + $(rk_str)-order RK" + end + + # 绘制多个数值解 + for (i, res) in enumerate(result_list) + style = plotter.default_styles["comparison"][mod1(i, length(plotter.default_styles["comparison"]))] + label = "Numerical ($(uppercase(res["config"]["scheme"]))$(res["config"]["order"]))" + plt.plot( + res["x"], res["numerical"]; + label=label, + style..., + markersize=5, linewidth=0.5 + ) + end + + # 绘制解析解 + plt.plot( + result_list[1]["x"], result_list[1]["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +function _set_common_style(plotter::CFDPlotter, title::String) + plt.title(title, fontsize=12) + plt.xlabel("x", fontsize=10) + plt.ylabel("u", fontsize=10) + plt.legend(fontsize=9) + plt.grid(true, color="gray", linestyle="--", linewidth=0.5, alpha=0.7) + plt.tight_layout() +end + +""" +快捷函数:ENO/WENO对比绘图 +""" +function plot_eno_weno_comparison(eno_result::Dict, weno_result::Dict; save_path=nothing) + plotter = CFDPlotter() + plot_comparison(plotter, [eno_result, weno_result]; save_path=save_path) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02e/reconstructor/eno.jl b/example/1d-linear-convection/weno3/julia/02e/reconstructor/eno.jl new file mode 100644 index 00000000..e78a636f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02e/reconstructor/eno.jl @@ -0,0 +1,107 @@ +# julia/reconstructor/eno.jl +""" +ENO 重构器(与 reconstructor/eno.py 完全同构) +""" + +# ---------------------- ENO 系数初始化 ---------------------- +function _init_eno_coef!(spatial_order::Int, coef::Matrix{Float64}) + if spatial_order == 1 + coef[1, 1] = 1.0 + coef[2, 1] = 1.0 + elseif spatial_order == 2 + coef[1, 1:2] = [3.0/2.0, -1.0/2.0] + coef[2, 1:2] = [1.0/2.0, 1.0/2.0] + coef[3, 1:2] = [-1.0/2.0, 3.0/2.0] + elseif spatial_order == 3 + coef[1, 1:3] = [11.0/6.0, -7.0/6.0, 1.0/3.0] + coef[2, 1:3] = [1.0/3.0, 5.0/6.0, -1.0/6.0] + coef[3, 1:3] = [-1.0/6.0, 5.0/6.0, 1.0/3.0] + coef[4, 1:3] = [1.0/3.0, -7.0/6.0, 11.0/6.0] + elseif spatial_order == 4 + coef[1, 1:4] = [25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0] + coef[2, 1:4] = [1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0] + coef[3, 1:4] = [-1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0] + coef[4, 1:4] = [1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0] + coef[5, 1:4] = [-1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0] + elseif spatial_order == 5 + coef[1, 1:5] = [137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0] + coef[2, 1:5] = [1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0] + coef[3, 1:5] = [-1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0] + coef[4, 1:5] = [1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0] + coef[5, 1:5] = [-1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0] + coef[6, 1:5] = [1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0] + elseif spatial_order == 6 + coef[1, 1:6] = [49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0] + coef[2, 1:6] = [1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0] + coef[3, 1:6] = [-1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0] + coef[4, 1:6] = [1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0] + coef[5, 1:6] = [-1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0] + coef[6, 1:6] = [1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0] + coef[7, 1:6] = [-1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0] + elseif spatial_order == 7 + coef[1, 1:7] = [363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0] + coef[2, 1:7] = [1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0] + coef[3, 1:7] = [-1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0] + coef[4, 1:7] = [1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0] + coef[5, 1:7] = [-1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0] + coef[6, 1:7] = [1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0] + coef[7, 1:7] = [-1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0] + coef[8, 1:7] = [1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0] + else + error("ENO 系数未实现 order=$spatial_order") + end +end + +# ---------------------- ENO 重构器 ---------------------- +mutable struct EnoReconstructor + spatial_order::Int + ntcells::Int + lmc::Vector{Int} + coef::Matrix{Float64} + dd::Matrix{Float64} + + function EnoReconstructor(spatial_order::Int, ntcells::Int) + lmc = zeros(Int, ntcells) + coef = zeros(Float64, spatial_order + 1, spatial_order) + dd = zeros(Float64, spatial_order, ntcells) + _init_eno_coef!(spatial_order, coef) + new(spatial_order, ntcells, lmc, coef, dd) + end +end + +function reconstruct(rec::EnoReconstructor, q::Vector{Float64}, cfd::Any) + # 1. 差商计算 (dd[1,:] = q) + @views rec.dd[1, :] .= q + for m in 2:rec.spatial_order + for j in 1:(rec.ntcells - m + 1) + rec.dd[m, j] = rec.dd[m-1, j+1] - rec.dd[m-1, j] + end + end + + # 2. 选择 smoothest stencil + domain = cfd.domain + for i in (domain.ist - 1):(domain.ied) # Python: range(ist-1, ied+1) → ied+1-1 = ied + rec.lmc[i] = i + for m in 2:rec.spatial_order + if abs(rec.dd[m, rec.lmc[i] - 1]) < abs(rec.dd[m, rec.lmc[i]]) + rec.lmc[i] -= 1 + end + end + end + + # 3. 重构界面值 + solution = cfd.solution + for i in domain.ist:(domain.ied) # Python: range(ist, ied+1) → ied+1-1 = ied + j = i - domain.ist + 1 # Julia 1-based + k1 = rec.lmc[i - 1] + k2 = rec.lmc[i] + r1 = (i - 1) - k1 + 1 + r2 = i - k2 + 1 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in 1:rec.spatial_order + solution.q_face_left[j] += q[k1 + m - 1] * rec.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m - 1] * rec.coef[r2, m] + end + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02e/reconstructor/weno3.jl b/example/1d-linear-convection/weno3/julia/02e/reconstructor/weno3.jl new file mode 100644 index 00000000..2b6fe1ab --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02e/reconstructor/weno3.jl @@ -0,0 +1,65 @@ +# julia/reconstructor/weno3.jl +""" +WENO3 重构器(与 reconstructor/weno3.py 完全同构) +""" + +mutable struct Weno3Reconstructor + # 无字段,与 Python 一致 +end + +function reconstruct(rec::Weno3Reconstructor, q::Vector{Float64}, cfd::Any) + domain = cfd.domain + solution = cfd.solution + _reconstruct_left_interfaces(domain, q, solution.q_face_left) + _reconstruct_right_interfaces(domain, q, solution.q_face_right) +end + +function _reconstruct_left_interfaces(domain, u, qL) + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in (domain.ist - 1):(domain.ied - 1) + j = i - (domain.ist - 1) + 1 # ← Julia 1-based: j = i - (ist-1) 对应 Python j = i - (ist-1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = _reconstruct_from_left_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_right_interfaces(domain, u, qR) + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in domain.ist:domain.ied + j = i - domain.ist + 1 # ← Julia 1-based: j = i - ist 对应 Python j = i - ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = _reconstruct_from_right_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_from_left_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -0.5*v1 + 1.5*v2 # r=1 stencil + q1 = 0.5*v2 + 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end + +function _reconstruct_from_right_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 0.5*v1 + 0.5*v2 # r=1 stencil + q1 = 1.5*v2 - 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02e/residual.jl b/example/1d-linear-convection/weno3/julia/02e/residual.jl new file mode 100644 index 00000000..e8fd6584 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02e/residual.jl @@ -0,0 +1,65 @@ +# julia/residual.jl +""" +残差计算器(与 residual.py 完全同构) +- 封装重建→通量→散度完整流程 +- 依赖 cfd 的多个字段 +""" + +include("mesh.jl") + +mutable struct ResidualCalculator + cfd::Any + config::Any + domain::Any + solution::Any + mesh::Mesh + reconstructor::Any + flux_calculator::Any # 通量计算器(外部传入,替代工厂) + + function ResidualCalculator(cfd::Any, flux_calculator::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + mesh = domain.mesh + reconstructor = cfd.reconstructor + + new(cfd, config, domain, solution, mesh, reconstructor, flux_calculator) + end +end + +""" +计算完整残差(对外唯一接口) +""" +function compute!(calc::ResidualCalculator) + _reconstruct(calc) + _compute_inviscid_flux(calc) + _compute_flux_divergence(calc) +end + +""" +私有方法:界面值重建 +""" +function _reconstruct(calc::ResidualCalculator) + reconstruct(calc.reconstructor, calc.solution.u, calc.cfd) +end + +""" +私有方法:计算无粘通量 +""" +function _compute_inviscid_flux(calc::ResidualCalculator) + compute!(calc.flux_calculator, + calc.solution.q_face_left, + calc.solution.q_face_right, + calc.solution.flux) +end + +""" +私有方法:计算通量散度(残差 = -dF/dx) +""" +function _compute_flux_divergence(calc::ResidualCalculator) + solution = calc.solution + mesh = calc.mesh + for i in 1:mesh.ncells + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / mesh.dx + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02e/run_eno_weno.jl b/example/1d-linear-convection/weno3/julia/02e/run_eno_weno.jl new file mode 100644 index 00000000..58b6cfa5 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02e/run_eno_weno.jl @@ -0,0 +1,50 @@ +# julia/run_eno_weno.jl +""" +1:1 复刻 run_eno_weno.py 的 Julia 版本 +""" + +include("config.jl") +include("mesh.jl") +include("solver.jl") +include("plotter.jl") + +function performEnoWenoAnalysis() + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + println("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + with_reconstruction(config_eno3, "eno", 3) # 显式指定 3 阶 + config_eno3.dt = 0.0025 # 覆盖默认值 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + run!(cfd_eno3) # 求解并生成 result 字典 + + # 3. 配置并运行 WENO3 求解 + println("Running WENO3 solver...") + config_weno3 = CfdConfig() + with_reconstruction(config_weno3, "weno", 3) # 显式指定 3 阶(WENO 默认 5 阶) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + run!(cfd_weno3) + + # 5. 绘制 ENO/WENO 对比图 + println("Plotting comparison results...") + plot_eno_weno_comparison( + cfd_eno3.result, + cfd_weno3.result; + save_path="eno_weno_comparison.png" + ) + + return cfd_eno3, cfd_weno3 +end + +# 主程序入口 +if abspath(PROGRAM_FILE) == @__FILE__ + performEnoWenoAnalysis() + println("Analysis completed!") +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02e/solution.jl b/example/1d-linear-convection/weno3/julia/02e/solution.jl new file mode 100644 index 00000000..d1f24e68 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02e/solution.jl @@ -0,0 +1,64 @@ +# julia/solution.jl +""" +Solution 结构体(与真实 solution.py 完全同构) +字段顺序、方法、逻辑 1:1 对齐 +""" + +include("mesh.jl") +include("domain.jl") +include("initial_condition.jl") + +mutable struct Solution + domain::Domain + q_face_left::Vector{Float64} + q_face_right::Vector{Float64} + flux::Vector{Float64} + res::Vector{Float64} + u::Vector{Float64} + un::Vector{Float64} + + function Solution(config::Any, domain::Domain) + mesh = domain.mesh + + q_face_left = zeros(Float64, mesh.nnodes) + q_face_right = zeros(Float64, mesh.nnodes) + flux = zeros(Float64, mesh.nnodes) + res = zeros(Float64, mesh.ncells) + u = zeros(Float64, domain.ntcells) + un = zeros(Float64, domain.ntcells) + + sol = new(domain, q_face_left, q_face_right, flux, res, u, un) + initialize_from_config(sol, config) + return sol + end +end + +""" +重置解数组为初始状态 +""" +function reset_solution(sol::Solution) + fill!(sol.u, 0.0) + fill!(sol.un, 0.0) +end + +""" +根据配置初始化场 +""" + +function initialize_from_config(sol::Solution, config::Any) + ic_type = getfield_safe(config, :ic_type, "step") + ic = InitialConditionFactory.create(ic_type, config) + apply(ic, sol) +end + +""" +更新旧场:un = u +""" +function update_old_field(sol::Solution) + sol.un .= sol.u # 等价于 Python 的 un[:] = u[:] +end + +# ---------------------- 辅助函数 ---------------------- +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02e/solver.jl b/example/1d-linear-convection/weno3/julia/02e/solver.jl new file mode 100644 index 00000000..ea015f6e --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02e/solver.jl @@ -0,0 +1,167 @@ +# julia/solver.jl +""" +CFD 求解器主类(与 solver.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("initial_condition.jl") +include("boundary.jl") +include("flux.jl") +include("residual.jl") +include("time_integration.jl") +include("reconstructor/eno.jl") +include("reconstructor/weno3.jl") + +# 导入工厂模块(必须在顶层!) +using .FluxCalculatorFactory +using .TimeIntegratorFactory +using .BoundaryConditionFactory + +# ---------------------- Cfd 求解器 ---------------------- +mutable struct Cfd + config::Any + domain::Domain + solution::Solution + reconstructor::Any + residual_calculator::ResidualCalculator + integrator::Any + boundary_condition::Any + result::Dict{String, Any} + + function Cfd(config::Any, mesh::Mesh) + domain = Domain(config, mesh) + solution = Solution(config, domain) + + # 在 Cfd 构造函数中 + # =============== 重建器创建 =============== + recon_scheme = config.recon_scheme + spatial_order = config.spatial_order + + # ✅ 模拟 Python ReconstructorFactory 的逻辑 + if recon_scheme == "weno" + recon_scheme = "weno$(spatial_order)" + end + + # 现在根据 recon_scheme 创建 + reconstructor = if recon_scheme == "eno" + EnoReconstructor(spatial_order, domain.ntcells) + elseif recon_scheme == "weno3" + Weno3Reconstructor() + else + error("不支持的重建格式: $recon_scheme") + end + + # 构造 cfd 上下文(NamedTuple) + cfd_context = ( + config = config, + domain = domain + ) + + # 创建通量计算器 + flux_calculator = FluxCalculatorFactory.create(cfd_context) + + # 残差计算器 + residual_calculator = ResidualCalculator( + (config=config, domain=domain, solution=solution, reconstructor=reconstructor), + flux_calculator + ) + + # 构建边界条件上下文 + bc_context = (config = config, domain = domain) + + # 使用工厂创建边界条件 + boundary_condition = BoundaryConditionFactory.create(bc_context) + + # 构建时间推进器所需上下文 + integrator_context = ( + config = config, + domain = domain, + solution = solution, + residual_calculator = residual_calculator, + boundary_condition = boundary_condition + ) + + # 使用工厂创建时间推进器 + integrator = TimeIntegratorFactory.create(integrator_context) + #@show typeof(integrator) + + # 注入 cfd 到 residual_calculator 和 integrator + residual_calculator.cfd = (config=config, domain=domain, solution=solution, reconstructor=reconstructor, residual_calculator=residual_calculator, integrator=integrator, boundary_condition=boundary_condition) + integrator.base.cfd = residual_calculator.cfd + + result = Dict{String, Any}() + new(config, domain, solution, reconstructor, residual_calculator, integrator, boundary_condition, result) + end +end + +""" +通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界 +""" +function exact_solution(cfd::Cfd) + x = cfd.domain.mesh.xcc + T = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = @. (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = if cfd.config.ic_type == "step" + StepFunctionIC(cfd.config) + elseif cfd.config.ic_type == "sin" + SineWaveIC(cfd.config) + elseif cfd.config.ic_type == "gaussian" + GaussianPulseIC(cfd.config) + else + error("未知初始条件: $(cfd.config.ic_type)") + end + + return evaluate_at(ic, x_shifted) +end + +""" +主求解循环 +""" +function run!(cfd::Cfd) + # 应用初始边界条件并同步 old field + apply!(cfd.boundary_condition, cfd.solution.u) + update_old_field(cfd.solution) + + t = 0.0 + dt_old = cfd.config.dt + dt = dt_old + + while t < cfd.config.final_time + if t + dt > cfd.config.final_time + dt = cfd.config.final_time - t + end + #@show t, dt, maximum(cfd.solution.u), minimum(cfd.solution.u) + # 执行时间步 + step(cfd.integrator, dt) + t += dt + end + + # 恢复 dt + cfd.config.dt = dt_old + + # 整理结果 + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied-1] # Python: [ist:ied] + analytical = exact_solution(cfd) + + cfd.result = Dict( + "x" => cfd.domain.mesh.xcc, + "numerical" => u_numerical, + "analytical" => analytical, + "config" => Dict( + "scheme" => cfd.config.recon_scheme, + "order" => cfd.config.spatial_order, + "rk_order" => cfd.config.rk_order, + "final_time" => cfd.config.final_time + ) + ) + + return u_numerical +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02e/time_integration.jl b/example/1d-linear-convection/weno3/julia/02e/time_integration.jl new file mode 100644 index 00000000..add564ba --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02e/time_integration.jl @@ -0,0 +1,169 @@ +# julia/time_integration.jl +""" +时间推进器模块(与 time_integration.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("residual.jl") +include("boundary.jl") + +# ---------------------- 抽象时间推进器基类 ---------------------- +abstract type TimeIntegrator end + +mutable struct TimeIntegratorBase <: TimeIntegrator + cfd::Any + config::Any + domain::Domain + solution::Solution + residual_calculator::Any # ResidualCalculator +end + +function TimeIntegratorBase(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + residual_calculator = cfd.residual_calculator + TimeIntegratorBase(cfd, config, domain, solution, residual_calculator) +end + +function compute_residual(integrator::TimeIntegratorBase) + compute!(integrator.residual_calculator) +end + +function apply_boundary(integrator::TimeIntegratorBase) + apply!(integrator.cfd.boundary_condition, integrator.solution.u) +end + +function map_idx(integrator::TimeIntegratorBase, i::Int) + return i - integrator.domain.ist + 1 # ← +1 转为 1-based +end + +# ---------------------- RK1Integrator ---------------------- +mutable struct RK1Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK1Integrator(cfd::Any) + RK1Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK1Integrator, dt::Float64) + compute_residual(integrator.base) + for i in integrator.base.domain.ist:(integrator.base.domain.ied - 1) + j = map_idx(integrator.base, i) + integrator.base.solution.u[i] += dt * integrator.base.solution.res[j] + end + apply_boundary(integrator.base) + update_old_field(integrator.base.solution) +end + +# ---------------------- RK2Integrator ---------------------- +mutable struct RK2Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK2Integrator(cfd::Any) + RK2Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK2Integrator, dt::Float64) + base = integrator.base + # 阶段1:预测步 + compute_residual(base) + u_pred = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u_pred[i] += dt * base.solution.res[j] + end + base.solution.u .= u_pred + apply_boundary(base) + # 阶段2:校正步 + compute_residual(base) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = 0.5 * base.solution.un[i] + 0.5 * base.solution.u[i] + 0.5 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- RK3Integrator ---------------------- +mutable struct RK3Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK3Integrator(cfd::Any) + RK3Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK3Integrator, dt::Float64) + base = integrator.base + # 阶段1 + compute_residual(base) + u1 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u1[i] += dt * base.solution.res[j] + end + base.solution.u .= u1 + apply_boundary(base) + # 阶段2 + compute_residual(base) + u2 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u2[i] = 0.75 * base.solution.un[i] + 0.25 * base.solution.u[i] + 0.25 * dt * base.solution.res[j] + end + base.solution.u .= u2 + apply_boundary(base) + # 阶段3 + compute_residual(base) + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = c1 * base.solution.un[i] + c2 * base.solution.u[i] + c3 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- TimeIntegratorFactory ---------------------- +module TimeIntegratorFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "时间积分器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + rk_order = cfd.config.rk_order + if !(rk_order isa Integer) || rk_order < 1 + error("rk_order 必须为正整数,当前值: $rk_order") + end + + name = "rk$rk_order" + if !haskey(_REGISTRY, name) + available = sort(collect(keys(_REGISTRY))) + error("未注册的时间积分器: '$name'。可用选项: $available") + end + + return _REGISTRY[name](cfd) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("rk1", cfd -> Main.RK1Integrator(cfd)) +register("rk2", cfd -> Main.RK2Integrator(cfd)) +register("rk3", cfd -> Main.RK3Integrator(cfd)) + +end # module TimeIntegratorFactory + +export TimeIntegratorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02f/boundary.jl b/example/1d-linear-convection/weno3/julia/02f/boundary.jl new file mode 100644 index 00000000..396c1264 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02f/boundary.jl @@ -0,0 +1,129 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end + + +# ---------------------- BoundaryConditionFactory ---------------------- +module BoundaryConditionFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "边界条件 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + bc_type = cfd.config.boundary_type + if !(bc_type isa AbstractString) + error("boundary_type 必须为字符串,当前值: $bc_type") + end + + if !haskey(_REGISTRY, bc_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的边界条件: '$bc_type'。可用选项: $available") + end + + return _REGISTRY[bc_type](cfd) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("periodic", cfd -> Main.PeriodicBoundary(cfd)) +register("dirichlet", cfd -> Main.DirichletBoundary(cfd)) +register("neumann", cfd -> Main.NeumannBoundary(cfd)) + +end # module BoundaryConditionFactory + +export BoundaryConditionFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02f/config.jl b/example/1d-linear-convection/weno3/julia/02f/config.jl new file mode 100644 index 00000000..bf48de3a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02f/config.jl @@ -0,0 +1,68 @@ +# julia/config.jl +""" +CfdConfig:与 Python config.py 完全同构 +""" +mutable struct CfdConfig + ic_type::String + recon_scheme::String + flux_type::String + rk_order::Int + wave_speed::Float64 + final_time::Float64 + dt::Float64 + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + spatial_order::Int + + function CfdConfig() + new( + "step", + "eno", + "rusanov", + 1, + 1.0, + 0.625, + 0.025, + "periodic", + 1.0, + 2.0, + 2 + ) + end +end + +""" +专用配置:重建方案(链式调用) +""" +function with_reconstruction(cfg::CfdConfig, scheme::String, order::Union{Int, Nothing}=nothing) + cfg.recon_scheme = lowercase(scheme) + + if order !== nothing + cfg.spatial_order = order + else + if startswith(cfg.recon_scheme, "weno") + cfg.spatial_order = 5 + elseif cfg.recon_scheme == "eno" + cfg.spatial_order = 3 + else + error("不支持的重建格式:$scheme(仅支持 eno/weno)") + end + end + + return cfg # 支持链式调用 +end + +""" +专用配置:边界条件(链式调用) +""" +function with_boundary(cfg::CfdConfig, bc_type::String; left_value=nothing, right_value=nothing) + cfg.boundary_type = bc_type + if left_value !== nothing + cfg.left_boundary_value = left_value + end + if right_value !== nothing + cfg.right_boundary_value = right_value + end + return cfg +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02f/domain.jl b/example/1d-linear-convection/weno3/julia/02f/domain.jl new file mode 100644 index 00000000..a7edc226 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02f/domain.jl @@ -0,0 +1,61 @@ +# julia/domain.jl +""" +Domain 结构体(与 domain.py 完全同构) +- 保存 config +- nghosts 计算逻辑 1:1 +- ist/ied 为 0-based(与 Python 一致) +""" + +include("mesh.jl") + +mutable struct Domain + config::Any # 保存 config(与 Python 一致) + mesh::Mesh + nghosts::Int + ist::Int # 0-based + ied::Int # 0-based + ntcells::Int +end + +function Domain(config::Any, mesh::Mesh) + nghosts = _calc_nghosts(config) + ist = nghosts + 1 # ← 1-based 起始索引 + ied = ist + mesh.ncells # ← 1-based 结束索引(不包含) + ntcells = mesh.ncells + 2 * nghosts + Domain(config, mesh, nghosts, ist, ied, ntcells) +end + +function _calc_nghosts(config::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + order = getfield_safe(config, :spatial_order, 2) + + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + if scheme == "eno" + nghosts = order + elseif startswith(scheme, "weno") + nghosts = order ÷ 2 + 1 + else + error("未知重建格式 $(scheme),无法计算ghost层!") + end + + if nghosts <= 0 + error("计算得到的ghost层数量无效:$(nghosts)(阶数$(order),格式$(scheme))") + end + + return nghosts +end + +function is_physical_cell(domain::Domain, idx::Int) + return domain.ist <= idx < domain.ied +end + +function get_physical_indices(domain::Domain) + return domain.ist:(domain.ied - 1) +end + +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02f/eno_weno_comparison.png b/example/1d-linear-convection/weno3/julia/02f/eno_weno_comparison.png new file mode 100644 index 00000000..00a3f0d3 Binary files /dev/null and b/example/1d-linear-convection/weno3/julia/02f/eno_weno_comparison.png differ diff --git a/example/1d-linear-convection/weno3/julia/02f/flux.jl b/example/1d-linear-convection/weno3/julia/02f/flux.jl new file mode 100644 index 00000000..cff23582 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02f/flux.jl @@ -0,0 +1,112 @@ +# julia/flux.jl +""" +通量计算器模块(与 flux.py 完全同构) +- 抽象基类 + 具体实现 +- 字段:cfd, config, mesh, wave_speed +""" + +include("mesh.jl") + +# ---------------------- 抽象基类 ---------------------- +""" +InviscidFluxCalculator 抽象类型 +Julia 无 ABC,用文档约定 +所有子类型必须实现 compute! +""" +abstract type InviscidFluxCalculator end + +# ---------------------- RusanovFluxCalculator ---------------------- +mutable struct RusanovFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function RusanovFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::RusanovFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = calc.wave_speed + c_R = calc.wave_speed + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + end +end + +# ---------------------- EngquistOsherFluxCalculator ---------------------- +mutable struct EngquistOsherFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function EngquistOsherFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::EngquistOsherFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + c = calc.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + end +end + +# ---------------------- FluxCalculatorFactory ---------------------- +module FluxCalculatorFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "通量计算器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + config = cfd.config + if !hasproperty(config, :flux_type) + error("cfd.config 缺少 flux_type 字段") + end + + flux_type = config.flux_type + if !(flux_type isa AbstractString) + error("flux_type 必须为字符串,当前值: $flux_type") + end + + if !haskey(_REGISTRY, flux_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的通量计算器: '$flux_type'。可用选项: $available") + end + + return _REGISTRY[flux_type](cfd) +end + +# ✅ 修正:使用 Main. 前缀 +register("rusanov", cfd -> Main.RusanovFluxCalculator(cfd)) +register("engquist-osher", cfd -> Main.EngquistOsherFluxCalculator(cfd)) + +end # module FluxCalculatorFactory + +export FluxCalculatorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02f/initial_condition.jl b/example/1d-linear-convection/weno3/julia/02f/initial_condition.jl new file mode 100644 index 00000000..aa120160 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02f/initial_condition.jl @@ -0,0 +1,120 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end + +# ---------------------- InitialConditionFactory ---------------------- +module InitialConditionFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "初始条件 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(ic_type::String, config::Any) + if !(ic_type isa AbstractString) + error("ic_type 必须为字符串,当前值: $ic_type") + end + + if !haskey(_REGISTRY, ic_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的初始条件: '$ic_type'。可用选项: $available") + end + + return _REGISTRY[ic_type](config) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("step", config -> Main.StepFunctionIC(config)) +register("sin", config -> Main.SineWaveIC(config)) +register("gaussian", config -> Main.GaussianPulseIC(config)) + +end # module InitialConditionFactory + +export InitialConditionFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02f/mesh.jl b/example/1d-linear-convection/weno3/julia/02f/mesh.jl new file mode 100644 index 00000000..1b0dd4bf --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02f/mesh.jl @@ -0,0 +1,45 @@ +# julia/mesh.jl +""" +Mesh 结构体(与提供的 mesh.py 完全同构) +- 字段名、初始化顺序、循环逻辑完全一致 +- 不做任何泛化或优化 +""" + +mutable struct Mesh + xmin::Float64 + xmax::Float64 + ncells::Int + nnodes::Int + nx::Int + x::Vector{Float64} + xcc::Vector{Float64} + L::Float64 # 在 init_mesh 中赋值 + dx::Float64 # 在 init_mesh 中赋值 + + function Mesh() + # 与 Python __init__ 完全一致 + xmin = 0.0 + xmax = 2.0 + ncells = 40 + nnodes = ncells + 1 + nx = ncells + x = zeros(Float64, nnodes) + xcc = zeros(Float64, ncells) + + # 调用 init_mesh(模拟 Python) + L = xmax - xmin + dx = L / ncells + + # Generate node coordinates: for i in range(self.nnodes) + for i in 1:nnodes + x[i] = xmin + (i - 1) * dx # Python i → Julia i-1 + end + + # Generate cell center coordinates + for i in 1:ncells + xcc[i] = 0.5 * (x[i] + x[i+1]) + end + + new(xmin, xmax, ncells, nnodes, nx, x, xcc, L, dx) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02f/plotter.jl b/example/1d-linear-convection/weno3/julia/02f/plotter.jl new file mode 100644 index 00000000..a77d8d68 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02f/plotter.jl @@ -0,0 +1,147 @@ +# julia/plotter.jl +""" +CFDPlotter 的 Julia 实现(通过 PythonCall.jl 调用 Matplotlib) +确保与 Python plotter.py 行为完全一致 +""" + +using PythonCall + +# 初始化 Python 环境(加载 matplotlib, inflect) +const plt = pyimport("matplotlib.pyplot") +const inflect = pyimport("inflect") + +mutable struct CFDPlotter + default_styles::Dict{String, Any} + p::Py +end + +function CFDPlotter() + default_styles = Dict{String, Any}( + "numerical" => Dict( + :color => "blue", + :linestyle => "-", + :marker => "o", + :markerfacecolor => "none" + ), + "analytical" => Dict( + :color => "red", + :linestyle => "--", + :marker => "", + :linewidth => 1.5 + ), + "comparison" => [ + Dict(:color => "black", :linestyle => "-", :marker => "o", :markerfacecolor => "none"), + Dict(:color => "blue", :linestyle => "--", :marker => "s", :markerfacecolor => "none"), + Dict(:color => "green", :linestyle => ":", :marker => "^", :markerfacecolor => "none") + ] + ) + p = inflect.engine() + CFDPlotter(default_styles, p) +end + +""" +轻量即时绘图(快速验证结果) +""" +function plot_quick(plotter::CFDPlotter, cfd_result::Dict; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + rk_order = cfd_result["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = cfd_result["config"]["final_time"] + order = cfd_result["config"]["order"] + scheme = uppercase(cfd_result["config"]["scheme"]) + actual_title = "1D Convection (t=$(final_time))\n$(order)th-order $(scheme) + $(rk_str)-order RK" + end + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"]; + label="Numerical ($(uppercase(cfd_result["config"]["scheme"])))", + plotter.default_styles["numerical"]..., + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + # 通用样式 + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +""" +多格式/多精度对比绘图 +""" +function plot_comparison(plotter::CFDPlotter, result_list::Vector{Dict{String, Any}}; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + schemes = [uppercase(r["config"]["scheme"]) * string(r["config"]["order"]) for r in result_list] + rk_order = result_list[1]["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = result_list[1]["config"]["final_time"] + actual_title = "1D Convection Comparison (t=$(final_time))\n$(join(schemes, ", ")) + $(rk_str)-order RK" + end + + # 绘制多个数值解 + for (i, res) in enumerate(result_list) + style = plotter.default_styles["comparison"][mod1(i, length(plotter.default_styles["comparison"]))] + label = "Numerical ($(uppercase(res["config"]["scheme"]))$(res["config"]["order"]))" + plt.plot( + res["x"], res["numerical"]; + label=label, + style..., + markersize=5, linewidth=0.5 + ) + end + + # 绘制解析解 + plt.plot( + result_list[1]["x"], result_list[1]["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +function _set_common_style(plotter::CFDPlotter, title::String) + plt.title(title, fontsize=12) + plt.xlabel("x", fontsize=10) + plt.ylabel("u", fontsize=10) + plt.legend(fontsize=9) + plt.grid(true, color="gray", linestyle="--", linewidth=0.5, alpha=0.7) + plt.tight_layout() +end + +""" +快捷函数:ENO/WENO对比绘图 +""" +function plot_eno_weno_comparison(eno_result::Dict, weno_result::Dict; save_path=nothing) + plotter = CFDPlotter() + plot_comparison(plotter, [eno_result, weno_result]; save_path=save_path) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02f/reconstructor/eno.jl b/example/1d-linear-convection/weno3/julia/02f/reconstructor/eno.jl new file mode 100644 index 00000000..e78a636f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02f/reconstructor/eno.jl @@ -0,0 +1,107 @@ +# julia/reconstructor/eno.jl +""" +ENO 重构器(与 reconstructor/eno.py 完全同构) +""" + +# ---------------------- ENO 系数初始化 ---------------------- +function _init_eno_coef!(spatial_order::Int, coef::Matrix{Float64}) + if spatial_order == 1 + coef[1, 1] = 1.0 + coef[2, 1] = 1.0 + elseif spatial_order == 2 + coef[1, 1:2] = [3.0/2.0, -1.0/2.0] + coef[2, 1:2] = [1.0/2.0, 1.0/2.0] + coef[3, 1:2] = [-1.0/2.0, 3.0/2.0] + elseif spatial_order == 3 + coef[1, 1:3] = [11.0/6.0, -7.0/6.0, 1.0/3.0] + coef[2, 1:3] = [1.0/3.0, 5.0/6.0, -1.0/6.0] + coef[3, 1:3] = [-1.0/6.0, 5.0/6.0, 1.0/3.0] + coef[4, 1:3] = [1.0/3.0, -7.0/6.0, 11.0/6.0] + elseif spatial_order == 4 + coef[1, 1:4] = [25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0] + coef[2, 1:4] = [1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0] + coef[3, 1:4] = [-1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0] + coef[4, 1:4] = [1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0] + coef[5, 1:4] = [-1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0] + elseif spatial_order == 5 + coef[1, 1:5] = [137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0] + coef[2, 1:5] = [1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0] + coef[3, 1:5] = [-1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0] + coef[4, 1:5] = [1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0] + coef[5, 1:5] = [-1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0] + coef[6, 1:5] = [1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0] + elseif spatial_order == 6 + coef[1, 1:6] = [49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0] + coef[2, 1:6] = [1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0] + coef[3, 1:6] = [-1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0] + coef[4, 1:6] = [1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0] + coef[5, 1:6] = [-1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0] + coef[6, 1:6] = [1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0] + coef[7, 1:6] = [-1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0] + elseif spatial_order == 7 + coef[1, 1:7] = [363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0] + coef[2, 1:7] = [1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0] + coef[3, 1:7] = [-1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0] + coef[4, 1:7] = [1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0] + coef[5, 1:7] = [-1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0] + coef[6, 1:7] = [1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0] + coef[7, 1:7] = [-1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0] + coef[8, 1:7] = [1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0] + else + error("ENO 系数未实现 order=$spatial_order") + end +end + +# ---------------------- ENO 重构器 ---------------------- +mutable struct EnoReconstructor + spatial_order::Int + ntcells::Int + lmc::Vector{Int} + coef::Matrix{Float64} + dd::Matrix{Float64} + + function EnoReconstructor(spatial_order::Int, ntcells::Int) + lmc = zeros(Int, ntcells) + coef = zeros(Float64, spatial_order + 1, spatial_order) + dd = zeros(Float64, spatial_order, ntcells) + _init_eno_coef!(spatial_order, coef) + new(spatial_order, ntcells, lmc, coef, dd) + end +end + +function reconstruct(rec::EnoReconstructor, q::Vector{Float64}, cfd::Any) + # 1. 差商计算 (dd[1,:] = q) + @views rec.dd[1, :] .= q + for m in 2:rec.spatial_order + for j in 1:(rec.ntcells - m + 1) + rec.dd[m, j] = rec.dd[m-1, j+1] - rec.dd[m-1, j] + end + end + + # 2. 选择 smoothest stencil + domain = cfd.domain + for i in (domain.ist - 1):(domain.ied) # Python: range(ist-1, ied+1) → ied+1-1 = ied + rec.lmc[i] = i + for m in 2:rec.spatial_order + if abs(rec.dd[m, rec.lmc[i] - 1]) < abs(rec.dd[m, rec.lmc[i]]) + rec.lmc[i] -= 1 + end + end + end + + # 3. 重构界面值 + solution = cfd.solution + for i in domain.ist:(domain.ied) # Python: range(ist, ied+1) → ied+1-1 = ied + j = i - domain.ist + 1 # Julia 1-based + k1 = rec.lmc[i - 1] + k2 = rec.lmc[i] + r1 = (i - 1) - k1 + 1 + r2 = i - k2 + 1 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in 1:rec.spatial_order + solution.q_face_left[j] += q[k1 + m - 1] * rec.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m - 1] * rec.coef[r2, m] + end + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02f/reconstructor/factory.jl b/example/1d-linear-convection/weno3/julia/02f/reconstructor/factory.jl new file mode 100644 index 00000000..f9c9cb4b --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02f/reconstructor/factory.jl @@ -0,0 +1,41 @@ +# julia/reconstructor/factory.jl + +""" +ReconstructorFactory +对标 Python: ReconstructorFactory.create(config, domain) +""" +module ReconstructorFactory + +# ✅ 不要用 using ..EnoReconstructor(它们不是模块) +# 直接通过 Main. 引用顶层定义的类型 + +function create(config::Any, domain::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + # 处理 WENO 默认命名(如 Python) + if scheme == "weno" + order = getfield_safe(config, :spatial_order, 5) + scheme = "weno$(order)" + end + + if scheme == "eno" + order = getfield_safe(config, :spatial_order, 3) + return Main.EnoReconstructor(order, domain.ntcells) + elseif scheme == "weno3" + return Main.Weno3Reconstructor() + else + error("不支持的重建格式: $scheme(仅支持 eno/weno3)") + end +end + +# 辅助函数(复制自 domain.jl) +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +end # module ReconstructorFactory + +export ReconstructorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02f/reconstructor/weno3.jl b/example/1d-linear-convection/weno3/julia/02f/reconstructor/weno3.jl new file mode 100644 index 00000000..2b6fe1ab --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02f/reconstructor/weno3.jl @@ -0,0 +1,65 @@ +# julia/reconstructor/weno3.jl +""" +WENO3 重构器(与 reconstructor/weno3.py 完全同构) +""" + +mutable struct Weno3Reconstructor + # 无字段,与 Python 一致 +end + +function reconstruct(rec::Weno3Reconstructor, q::Vector{Float64}, cfd::Any) + domain = cfd.domain + solution = cfd.solution + _reconstruct_left_interfaces(domain, q, solution.q_face_left) + _reconstruct_right_interfaces(domain, q, solution.q_face_right) +end + +function _reconstruct_left_interfaces(domain, u, qL) + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in (domain.ist - 1):(domain.ied - 1) + j = i - (domain.ist - 1) + 1 # ← Julia 1-based: j = i - (ist-1) 对应 Python j = i - (ist-1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = _reconstruct_from_left_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_right_interfaces(domain, u, qR) + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in domain.ist:domain.ied + j = i - domain.ist + 1 # ← Julia 1-based: j = i - ist 对应 Python j = i - ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = _reconstruct_from_right_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_from_left_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -0.5*v1 + 1.5*v2 # r=1 stencil + q1 = 0.5*v2 + 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end + +function _reconstruct_from_right_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 0.5*v1 + 0.5*v2 # r=1 stencil + q1 = 1.5*v2 - 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02f/residual.jl b/example/1d-linear-convection/weno3/julia/02f/residual.jl new file mode 100644 index 00000000..e8fd6584 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02f/residual.jl @@ -0,0 +1,65 @@ +# julia/residual.jl +""" +残差计算器(与 residual.py 完全同构) +- 封装重建→通量→散度完整流程 +- 依赖 cfd 的多个字段 +""" + +include("mesh.jl") + +mutable struct ResidualCalculator + cfd::Any + config::Any + domain::Any + solution::Any + mesh::Mesh + reconstructor::Any + flux_calculator::Any # 通量计算器(外部传入,替代工厂) + + function ResidualCalculator(cfd::Any, flux_calculator::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + mesh = domain.mesh + reconstructor = cfd.reconstructor + + new(cfd, config, domain, solution, mesh, reconstructor, flux_calculator) + end +end + +""" +计算完整残差(对外唯一接口) +""" +function compute!(calc::ResidualCalculator) + _reconstruct(calc) + _compute_inviscid_flux(calc) + _compute_flux_divergence(calc) +end + +""" +私有方法:界面值重建 +""" +function _reconstruct(calc::ResidualCalculator) + reconstruct(calc.reconstructor, calc.solution.u, calc.cfd) +end + +""" +私有方法:计算无粘通量 +""" +function _compute_inviscid_flux(calc::ResidualCalculator) + compute!(calc.flux_calculator, + calc.solution.q_face_left, + calc.solution.q_face_right, + calc.solution.flux) +end + +""" +私有方法:计算通量散度(残差 = -dF/dx) +""" +function _compute_flux_divergence(calc::ResidualCalculator) + solution = calc.solution + mesh = calc.mesh + for i in 1:mesh.ncells + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / mesh.dx + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02f/run_eno_weno.jl b/example/1d-linear-convection/weno3/julia/02f/run_eno_weno.jl new file mode 100644 index 00000000..58b6cfa5 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02f/run_eno_weno.jl @@ -0,0 +1,50 @@ +# julia/run_eno_weno.jl +""" +1:1 复刻 run_eno_weno.py 的 Julia 版本 +""" + +include("config.jl") +include("mesh.jl") +include("solver.jl") +include("plotter.jl") + +function performEnoWenoAnalysis() + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + println("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + with_reconstruction(config_eno3, "eno", 3) # 显式指定 3 阶 + config_eno3.dt = 0.0025 # 覆盖默认值 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + run!(cfd_eno3) # 求解并生成 result 字典 + + # 3. 配置并运行 WENO3 求解 + println("Running WENO3 solver...") + config_weno3 = CfdConfig() + with_reconstruction(config_weno3, "weno", 3) # 显式指定 3 阶(WENO 默认 5 阶) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + run!(cfd_weno3) + + # 5. 绘制 ENO/WENO 对比图 + println("Plotting comparison results...") + plot_eno_weno_comparison( + cfd_eno3.result, + cfd_weno3.result; + save_path="eno_weno_comparison.png" + ) + + return cfd_eno3, cfd_weno3 +end + +# 主程序入口 +if abspath(PROGRAM_FILE) == @__FILE__ + performEnoWenoAnalysis() + println("Analysis completed!") +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02f/solution.jl b/example/1d-linear-convection/weno3/julia/02f/solution.jl new file mode 100644 index 00000000..d1f24e68 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02f/solution.jl @@ -0,0 +1,64 @@ +# julia/solution.jl +""" +Solution 结构体(与真实 solution.py 完全同构) +字段顺序、方法、逻辑 1:1 对齐 +""" + +include("mesh.jl") +include("domain.jl") +include("initial_condition.jl") + +mutable struct Solution + domain::Domain + q_face_left::Vector{Float64} + q_face_right::Vector{Float64} + flux::Vector{Float64} + res::Vector{Float64} + u::Vector{Float64} + un::Vector{Float64} + + function Solution(config::Any, domain::Domain) + mesh = domain.mesh + + q_face_left = zeros(Float64, mesh.nnodes) + q_face_right = zeros(Float64, mesh.nnodes) + flux = zeros(Float64, mesh.nnodes) + res = zeros(Float64, mesh.ncells) + u = zeros(Float64, domain.ntcells) + un = zeros(Float64, domain.ntcells) + + sol = new(domain, q_face_left, q_face_right, flux, res, u, un) + initialize_from_config(sol, config) + return sol + end +end + +""" +重置解数组为初始状态 +""" +function reset_solution(sol::Solution) + fill!(sol.u, 0.0) + fill!(sol.un, 0.0) +end + +""" +根据配置初始化场 +""" + +function initialize_from_config(sol::Solution, config::Any) + ic_type = getfield_safe(config, :ic_type, "step") + ic = InitialConditionFactory.create(ic_type, config) + apply(ic, sol) +end + +""" +更新旧场:un = u +""" +function update_old_field(sol::Solution) + sol.un .= sol.u # 等价于 Python 的 un[:] = u[:] +end + +# ---------------------- 辅助函数 ---------------------- +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02f/solver.jl b/example/1d-linear-convection/weno3/julia/02f/solver.jl new file mode 100644 index 00000000..99fd60f6 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02f/solver.jl @@ -0,0 +1,152 @@ +# julia/solver.jl +""" +CFD 求解器主类(与 solver.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("initial_condition.jl") +include("boundary.jl") +include("flux.jl") +include("residual.jl") +include("time_integration.jl") +include("reconstructor/eno.jl") +include("reconstructor/weno3.jl") +include("reconstructor/factory.jl") + +# 导入工厂模块(必须在顶层!) +using .FluxCalculatorFactory +using .TimeIntegratorFactory +using .BoundaryConditionFactory +using .ReconstructorFactory + +# ---------------------- Cfd 求解器 ---------------------- +mutable struct Cfd + config::Any + domain::Domain + solution::Solution + reconstructor::Any + residual_calculator::ResidualCalculator + integrator::Any + boundary_condition::Any + result::Dict{String, Any} + + function Cfd(config::Any, mesh::Mesh) + domain = Domain(config, mesh) + solution = Solution(config, domain) + + reconstructor = ReconstructorFactory.create(config, domain) + + # 构造 cfd 上下文(NamedTuple) + cfd_context = ( + config = config, + domain = domain + ) + + # 创建通量计算器 + flux_calculator = FluxCalculatorFactory.create(cfd_context) + + # 残差计算器 + residual_calculator = ResidualCalculator( + (config=config, domain=domain, solution=solution, reconstructor=reconstructor), + flux_calculator + ) + + # 构建边界条件上下文 + bc_context = (config = config, domain = domain) + + # 使用工厂创建边界条件 + boundary_condition = BoundaryConditionFactory.create(bc_context) + + # 构建时间推进器所需上下文 + integrator_context = ( + config = config, + domain = domain, + solution = solution, + residual_calculator = residual_calculator, + boundary_condition = boundary_condition + ) + + # 使用工厂创建时间推进器 + integrator = TimeIntegratorFactory.create(integrator_context) + #@show typeof(integrator) + + # 注入 cfd 到 residual_calculator 和 integrator + residual_calculator.cfd = (config=config, domain=domain, solution=solution, reconstructor=reconstructor, residual_calculator=residual_calculator, integrator=integrator, boundary_condition=boundary_condition) + integrator.base.cfd = residual_calculator.cfd + + result = Dict{String, Any}() + new(config, domain, solution, reconstructor, residual_calculator, integrator, boundary_condition, result) + end +end + +""" +通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界 +""" +function exact_solution(cfd::Cfd) + x = cfd.domain.mesh.xcc + T = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = @. (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = if cfd.config.ic_type == "step" + StepFunctionIC(cfd.config) + elseif cfd.config.ic_type == "sin" + SineWaveIC(cfd.config) + elseif cfd.config.ic_type == "gaussian" + GaussianPulseIC(cfd.config) + else + error("未知初始条件: $(cfd.config.ic_type)") + end + + return evaluate_at(ic, x_shifted) +end + +""" +主求解循环 +""" +function run!(cfd::Cfd) + # 应用初始边界条件并同步 old field + apply!(cfd.boundary_condition, cfd.solution.u) + update_old_field(cfd.solution) + + t = 0.0 + dt_old = cfd.config.dt + dt = dt_old + + while t < cfd.config.final_time + if t + dt > cfd.config.final_time + dt = cfd.config.final_time - t + end + #@show t, dt, maximum(cfd.solution.u), minimum(cfd.solution.u) + # 执行时间步 + step(cfd.integrator, dt) + t += dt + end + + # 恢复 dt + cfd.config.dt = dt_old + + # 整理结果 + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied-1] # Python: [ist:ied] + analytical = exact_solution(cfd) + + cfd.result = Dict( + "x" => cfd.domain.mesh.xcc, + "numerical" => u_numerical, + "analytical" => analytical, + "config" => Dict( + "scheme" => cfd.config.recon_scheme, + "order" => cfd.config.spatial_order, + "rk_order" => cfd.config.rk_order, + "final_time" => cfd.config.final_time + ) + ) + + return u_numerical +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02f/time_integration.jl b/example/1d-linear-convection/weno3/julia/02f/time_integration.jl new file mode 100644 index 00000000..add564ba --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02f/time_integration.jl @@ -0,0 +1,169 @@ +# julia/time_integration.jl +""" +时间推进器模块(与 time_integration.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("residual.jl") +include("boundary.jl") + +# ---------------------- 抽象时间推进器基类 ---------------------- +abstract type TimeIntegrator end + +mutable struct TimeIntegratorBase <: TimeIntegrator + cfd::Any + config::Any + domain::Domain + solution::Solution + residual_calculator::Any # ResidualCalculator +end + +function TimeIntegratorBase(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + residual_calculator = cfd.residual_calculator + TimeIntegratorBase(cfd, config, domain, solution, residual_calculator) +end + +function compute_residual(integrator::TimeIntegratorBase) + compute!(integrator.residual_calculator) +end + +function apply_boundary(integrator::TimeIntegratorBase) + apply!(integrator.cfd.boundary_condition, integrator.solution.u) +end + +function map_idx(integrator::TimeIntegratorBase, i::Int) + return i - integrator.domain.ist + 1 # ← +1 转为 1-based +end + +# ---------------------- RK1Integrator ---------------------- +mutable struct RK1Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK1Integrator(cfd::Any) + RK1Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK1Integrator, dt::Float64) + compute_residual(integrator.base) + for i in integrator.base.domain.ist:(integrator.base.domain.ied - 1) + j = map_idx(integrator.base, i) + integrator.base.solution.u[i] += dt * integrator.base.solution.res[j] + end + apply_boundary(integrator.base) + update_old_field(integrator.base.solution) +end + +# ---------------------- RK2Integrator ---------------------- +mutable struct RK2Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK2Integrator(cfd::Any) + RK2Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK2Integrator, dt::Float64) + base = integrator.base + # 阶段1:预测步 + compute_residual(base) + u_pred = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u_pred[i] += dt * base.solution.res[j] + end + base.solution.u .= u_pred + apply_boundary(base) + # 阶段2:校正步 + compute_residual(base) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = 0.5 * base.solution.un[i] + 0.5 * base.solution.u[i] + 0.5 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- RK3Integrator ---------------------- +mutable struct RK3Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK3Integrator(cfd::Any) + RK3Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK3Integrator, dt::Float64) + base = integrator.base + # 阶段1 + compute_residual(base) + u1 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u1[i] += dt * base.solution.res[j] + end + base.solution.u .= u1 + apply_boundary(base) + # 阶段2 + compute_residual(base) + u2 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u2[i] = 0.75 * base.solution.un[i] + 0.25 * base.solution.u[i] + 0.25 * dt * base.solution.res[j] + end + base.solution.u .= u2 + apply_boundary(base) + # 阶段3 + compute_residual(base) + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = c1 * base.solution.un[i] + c2 * base.solution.u[i] + c3 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- TimeIntegratorFactory ---------------------- +module TimeIntegratorFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "时间积分器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + rk_order = cfd.config.rk_order + if !(rk_order isa Integer) || rk_order < 1 + error("rk_order 必须为正整数,当前值: $rk_order") + end + + name = "rk$rk_order" + if !haskey(_REGISTRY, name) + available = sort(collect(keys(_REGISTRY))) + error("未注册的时间积分器: '$name'。可用选项: $available") + end + + return _REGISTRY[name](cfd) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("rk1", cfd -> Main.RK1Integrator(cfd)) +register("rk2", cfd -> Main.RK2Integrator(cfd)) +register("rk3", cfd -> Main.RK3Integrator(cfd)) + +end # module TimeIntegratorFactory + +export TimeIntegratorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02g/boundary.jl b/example/1d-linear-convection/weno3/julia/02g/boundary.jl new file mode 100644 index 00000000..396c1264 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02g/boundary.jl @@ -0,0 +1,129 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end + + +# ---------------------- BoundaryConditionFactory ---------------------- +module BoundaryConditionFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "边界条件 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + bc_type = cfd.config.boundary_type + if !(bc_type isa AbstractString) + error("boundary_type 必须为字符串,当前值: $bc_type") + end + + if !haskey(_REGISTRY, bc_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的边界条件: '$bc_type'。可用选项: $available") + end + + return _REGISTRY[bc_type](cfd) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("periodic", cfd -> Main.PeriodicBoundary(cfd)) +register("dirichlet", cfd -> Main.DirichletBoundary(cfd)) +register("neumann", cfd -> Main.NeumannBoundary(cfd)) + +end # module BoundaryConditionFactory + +export BoundaryConditionFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02g/config.jl b/example/1d-linear-convection/weno3/julia/02g/config.jl new file mode 100644 index 00000000..bf48de3a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02g/config.jl @@ -0,0 +1,68 @@ +# julia/config.jl +""" +CfdConfig:与 Python config.py 完全同构 +""" +mutable struct CfdConfig + ic_type::String + recon_scheme::String + flux_type::String + rk_order::Int + wave_speed::Float64 + final_time::Float64 + dt::Float64 + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + spatial_order::Int + + function CfdConfig() + new( + "step", + "eno", + "rusanov", + 1, + 1.0, + 0.625, + 0.025, + "periodic", + 1.0, + 2.0, + 2 + ) + end +end + +""" +专用配置:重建方案(链式调用) +""" +function with_reconstruction(cfg::CfdConfig, scheme::String, order::Union{Int, Nothing}=nothing) + cfg.recon_scheme = lowercase(scheme) + + if order !== nothing + cfg.spatial_order = order + else + if startswith(cfg.recon_scheme, "weno") + cfg.spatial_order = 5 + elseif cfg.recon_scheme == "eno" + cfg.spatial_order = 3 + else + error("不支持的重建格式:$scheme(仅支持 eno/weno)") + end + end + + return cfg # 支持链式调用 +end + +""" +专用配置:边界条件(链式调用) +""" +function with_boundary(cfg::CfdConfig, bc_type::String; left_value=nothing, right_value=nothing) + cfg.boundary_type = bc_type + if left_value !== nothing + cfg.left_boundary_value = left_value + end + if right_value !== nothing + cfg.right_boundary_value = right_value + end + return cfg +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02g/domain.jl b/example/1d-linear-convection/weno3/julia/02g/domain.jl new file mode 100644 index 00000000..a7edc226 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02g/domain.jl @@ -0,0 +1,61 @@ +# julia/domain.jl +""" +Domain 结构体(与 domain.py 完全同构) +- 保存 config +- nghosts 计算逻辑 1:1 +- ist/ied 为 0-based(与 Python 一致) +""" + +include("mesh.jl") + +mutable struct Domain + config::Any # 保存 config(与 Python 一致) + mesh::Mesh + nghosts::Int + ist::Int # 0-based + ied::Int # 0-based + ntcells::Int +end + +function Domain(config::Any, mesh::Mesh) + nghosts = _calc_nghosts(config) + ist = nghosts + 1 # ← 1-based 起始索引 + ied = ist + mesh.ncells # ← 1-based 结束索引(不包含) + ntcells = mesh.ncells + 2 * nghosts + Domain(config, mesh, nghosts, ist, ied, ntcells) +end + +function _calc_nghosts(config::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + order = getfield_safe(config, :spatial_order, 2) + + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + if scheme == "eno" + nghosts = order + elseif startswith(scheme, "weno") + nghosts = order ÷ 2 + 1 + else + error("未知重建格式 $(scheme),无法计算ghost层!") + end + + if nghosts <= 0 + error("计算得到的ghost层数量无效:$(nghosts)(阶数$(order),格式$(scheme))") + end + + return nghosts +end + +function is_physical_cell(domain::Domain, idx::Int) + return domain.ist <= idx < domain.ied +end + +function get_physical_indices(domain::Domain) + return domain.ist:(domain.ied - 1) +end + +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02g/flux.jl b/example/1d-linear-convection/weno3/julia/02g/flux.jl new file mode 100644 index 00000000..cff23582 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02g/flux.jl @@ -0,0 +1,112 @@ +# julia/flux.jl +""" +通量计算器模块(与 flux.py 完全同构) +- 抽象基类 + 具体实现 +- 字段:cfd, config, mesh, wave_speed +""" + +include("mesh.jl") + +# ---------------------- 抽象基类 ---------------------- +""" +InviscidFluxCalculator 抽象类型 +Julia 无 ABC,用文档约定 +所有子类型必须实现 compute! +""" +abstract type InviscidFluxCalculator end + +# ---------------------- RusanovFluxCalculator ---------------------- +mutable struct RusanovFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function RusanovFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::RusanovFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = calc.wave_speed + c_R = calc.wave_speed + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + end +end + +# ---------------------- EngquistOsherFluxCalculator ---------------------- +mutable struct EngquistOsherFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function EngquistOsherFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::EngquistOsherFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + c = calc.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + end +end + +# ---------------------- FluxCalculatorFactory ---------------------- +module FluxCalculatorFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "通量计算器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + config = cfd.config + if !hasproperty(config, :flux_type) + error("cfd.config 缺少 flux_type 字段") + end + + flux_type = config.flux_type + if !(flux_type isa AbstractString) + error("flux_type 必须为字符串,当前值: $flux_type") + end + + if !haskey(_REGISTRY, flux_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的通量计算器: '$flux_type'。可用选项: $available") + end + + return _REGISTRY[flux_type](cfd) +end + +# ✅ 修正:使用 Main. 前缀 +register("rusanov", cfd -> Main.RusanovFluxCalculator(cfd)) +register("engquist-osher", cfd -> Main.EngquistOsherFluxCalculator(cfd)) + +end # module FluxCalculatorFactory + +export FluxCalculatorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02g/initial_condition.jl b/example/1d-linear-convection/weno3/julia/02g/initial_condition.jl new file mode 100644 index 00000000..aa120160 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02g/initial_condition.jl @@ -0,0 +1,120 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end + +# ---------------------- InitialConditionFactory ---------------------- +module InitialConditionFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "初始条件 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(ic_type::String, config::Any) + if !(ic_type isa AbstractString) + error("ic_type 必须为字符串,当前值: $ic_type") + end + + if !haskey(_REGISTRY, ic_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的初始条件: '$ic_type'。可用选项: $available") + end + + return _REGISTRY[ic_type](config) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("step", config -> Main.StepFunctionIC(config)) +register("sin", config -> Main.SineWaveIC(config)) +register("gaussian", config -> Main.GaussianPulseIC(config)) + +end # module InitialConditionFactory + +export InitialConditionFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02g/mesh.jl b/example/1d-linear-convection/weno3/julia/02g/mesh.jl new file mode 100644 index 00000000..1b0dd4bf --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02g/mesh.jl @@ -0,0 +1,45 @@ +# julia/mesh.jl +""" +Mesh 结构体(与提供的 mesh.py 完全同构) +- 字段名、初始化顺序、循环逻辑完全一致 +- 不做任何泛化或优化 +""" + +mutable struct Mesh + xmin::Float64 + xmax::Float64 + ncells::Int + nnodes::Int + nx::Int + x::Vector{Float64} + xcc::Vector{Float64} + L::Float64 # 在 init_mesh 中赋值 + dx::Float64 # 在 init_mesh 中赋值 + + function Mesh() + # 与 Python __init__ 完全一致 + xmin = 0.0 + xmax = 2.0 + ncells = 40 + nnodes = ncells + 1 + nx = ncells + x = zeros(Float64, nnodes) + xcc = zeros(Float64, ncells) + + # 调用 init_mesh(模拟 Python) + L = xmax - xmin + dx = L / ncells + + # Generate node coordinates: for i in range(self.nnodes) + for i in 1:nnodes + x[i] = xmin + (i - 1) * dx # Python i → Julia i-1 + end + + # Generate cell center coordinates + for i in 1:ncells + xcc[i] = 0.5 * (x[i] + x[i+1]) + end + + new(xmin, xmax, ncells, nnodes, nx, x, xcc, L, dx) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02g/plotter.jl b/example/1d-linear-convection/weno3/julia/02g/plotter.jl new file mode 100644 index 00000000..a77d8d68 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02g/plotter.jl @@ -0,0 +1,147 @@ +# julia/plotter.jl +""" +CFDPlotter 的 Julia 实现(通过 PythonCall.jl 调用 Matplotlib) +确保与 Python plotter.py 行为完全一致 +""" + +using PythonCall + +# 初始化 Python 环境(加载 matplotlib, inflect) +const plt = pyimport("matplotlib.pyplot") +const inflect = pyimport("inflect") + +mutable struct CFDPlotter + default_styles::Dict{String, Any} + p::Py +end + +function CFDPlotter() + default_styles = Dict{String, Any}( + "numerical" => Dict( + :color => "blue", + :linestyle => "-", + :marker => "o", + :markerfacecolor => "none" + ), + "analytical" => Dict( + :color => "red", + :linestyle => "--", + :marker => "", + :linewidth => 1.5 + ), + "comparison" => [ + Dict(:color => "black", :linestyle => "-", :marker => "o", :markerfacecolor => "none"), + Dict(:color => "blue", :linestyle => "--", :marker => "s", :markerfacecolor => "none"), + Dict(:color => "green", :linestyle => ":", :marker => "^", :markerfacecolor => "none") + ] + ) + p = inflect.engine() + CFDPlotter(default_styles, p) +end + +""" +轻量即时绘图(快速验证结果) +""" +function plot_quick(plotter::CFDPlotter, cfd_result::Dict; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + rk_order = cfd_result["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = cfd_result["config"]["final_time"] + order = cfd_result["config"]["order"] + scheme = uppercase(cfd_result["config"]["scheme"]) + actual_title = "1D Convection (t=$(final_time))\n$(order)th-order $(scheme) + $(rk_str)-order RK" + end + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"]; + label="Numerical ($(uppercase(cfd_result["config"]["scheme"])))", + plotter.default_styles["numerical"]..., + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + # 通用样式 + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +""" +多格式/多精度对比绘图 +""" +function plot_comparison(plotter::CFDPlotter, result_list::Vector{Dict{String, Any}}; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + schemes = [uppercase(r["config"]["scheme"]) * string(r["config"]["order"]) for r in result_list] + rk_order = result_list[1]["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = result_list[1]["config"]["final_time"] + actual_title = "1D Convection Comparison (t=$(final_time))\n$(join(schemes, ", ")) + $(rk_str)-order RK" + end + + # 绘制多个数值解 + for (i, res) in enumerate(result_list) + style = plotter.default_styles["comparison"][mod1(i, length(plotter.default_styles["comparison"]))] + label = "Numerical ($(uppercase(res["config"]["scheme"]))$(res["config"]["order"]))" + plt.plot( + res["x"], res["numerical"]; + label=label, + style..., + markersize=5, linewidth=0.5 + ) + end + + # 绘制解析解 + plt.plot( + result_list[1]["x"], result_list[1]["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +function _set_common_style(plotter::CFDPlotter, title::String) + plt.title(title, fontsize=12) + plt.xlabel("x", fontsize=10) + plt.ylabel("u", fontsize=10) + plt.legend(fontsize=9) + plt.grid(true, color="gray", linestyle="--", linewidth=0.5, alpha=0.7) + plt.tight_layout() +end + +""" +快捷函数:ENO/WENO对比绘图 +""" +function plot_eno_weno_comparison(eno_result::Dict, weno_result::Dict; save_path=nothing) + plotter = CFDPlotter() + plot_comparison(plotter, [eno_result, weno_result]; save_path=save_path) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02g/reconstructor/eno.jl b/example/1d-linear-convection/weno3/julia/02g/reconstructor/eno.jl new file mode 100644 index 00000000..e78a636f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02g/reconstructor/eno.jl @@ -0,0 +1,107 @@ +# julia/reconstructor/eno.jl +""" +ENO 重构器(与 reconstructor/eno.py 完全同构) +""" + +# ---------------------- ENO 系数初始化 ---------------------- +function _init_eno_coef!(spatial_order::Int, coef::Matrix{Float64}) + if spatial_order == 1 + coef[1, 1] = 1.0 + coef[2, 1] = 1.0 + elseif spatial_order == 2 + coef[1, 1:2] = [3.0/2.0, -1.0/2.0] + coef[2, 1:2] = [1.0/2.0, 1.0/2.0] + coef[3, 1:2] = [-1.0/2.0, 3.0/2.0] + elseif spatial_order == 3 + coef[1, 1:3] = [11.0/6.0, -7.0/6.0, 1.0/3.0] + coef[2, 1:3] = [1.0/3.0, 5.0/6.0, -1.0/6.0] + coef[3, 1:3] = [-1.0/6.0, 5.0/6.0, 1.0/3.0] + coef[4, 1:3] = [1.0/3.0, -7.0/6.0, 11.0/6.0] + elseif spatial_order == 4 + coef[1, 1:4] = [25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0] + coef[2, 1:4] = [1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0] + coef[3, 1:4] = [-1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0] + coef[4, 1:4] = [1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0] + coef[5, 1:4] = [-1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0] + elseif spatial_order == 5 + coef[1, 1:5] = [137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0] + coef[2, 1:5] = [1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0] + coef[3, 1:5] = [-1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0] + coef[4, 1:5] = [1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0] + coef[5, 1:5] = [-1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0] + coef[6, 1:5] = [1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0] + elseif spatial_order == 6 + coef[1, 1:6] = [49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0] + coef[2, 1:6] = [1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0] + coef[3, 1:6] = [-1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0] + coef[4, 1:6] = [1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0] + coef[5, 1:6] = [-1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0] + coef[6, 1:6] = [1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0] + coef[7, 1:6] = [-1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0] + elseif spatial_order == 7 + coef[1, 1:7] = [363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0] + coef[2, 1:7] = [1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0] + coef[3, 1:7] = [-1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0] + coef[4, 1:7] = [1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0] + coef[5, 1:7] = [-1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0] + coef[6, 1:7] = [1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0] + coef[7, 1:7] = [-1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0] + coef[8, 1:7] = [1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0] + else + error("ENO 系数未实现 order=$spatial_order") + end +end + +# ---------------------- ENO 重构器 ---------------------- +mutable struct EnoReconstructor + spatial_order::Int + ntcells::Int + lmc::Vector{Int} + coef::Matrix{Float64} + dd::Matrix{Float64} + + function EnoReconstructor(spatial_order::Int, ntcells::Int) + lmc = zeros(Int, ntcells) + coef = zeros(Float64, spatial_order + 1, spatial_order) + dd = zeros(Float64, spatial_order, ntcells) + _init_eno_coef!(spatial_order, coef) + new(spatial_order, ntcells, lmc, coef, dd) + end +end + +function reconstruct(rec::EnoReconstructor, q::Vector{Float64}, cfd::Any) + # 1. 差商计算 (dd[1,:] = q) + @views rec.dd[1, :] .= q + for m in 2:rec.spatial_order + for j in 1:(rec.ntcells - m + 1) + rec.dd[m, j] = rec.dd[m-1, j+1] - rec.dd[m-1, j] + end + end + + # 2. 选择 smoothest stencil + domain = cfd.domain + for i in (domain.ist - 1):(domain.ied) # Python: range(ist-1, ied+1) → ied+1-1 = ied + rec.lmc[i] = i + for m in 2:rec.spatial_order + if abs(rec.dd[m, rec.lmc[i] - 1]) < abs(rec.dd[m, rec.lmc[i]]) + rec.lmc[i] -= 1 + end + end + end + + # 3. 重构界面值 + solution = cfd.solution + for i in domain.ist:(domain.ied) # Python: range(ist, ied+1) → ied+1-1 = ied + j = i - domain.ist + 1 # Julia 1-based + k1 = rec.lmc[i - 1] + k2 = rec.lmc[i] + r1 = (i - 1) - k1 + 1 + r2 = i - k2 + 1 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in 1:rec.spatial_order + solution.q_face_left[j] += q[k1 + m - 1] * rec.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m - 1] * rec.coef[r2, m] + end + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02g/reconstructor/factory.jl b/example/1d-linear-convection/weno3/julia/02g/reconstructor/factory.jl new file mode 100644 index 00000000..f9c9cb4b --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02g/reconstructor/factory.jl @@ -0,0 +1,41 @@ +# julia/reconstructor/factory.jl + +""" +ReconstructorFactory +对标 Python: ReconstructorFactory.create(config, domain) +""" +module ReconstructorFactory + +# ✅ 不要用 using ..EnoReconstructor(它们不是模块) +# 直接通过 Main. 引用顶层定义的类型 + +function create(config::Any, domain::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + # 处理 WENO 默认命名(如 Python) + if scheme == "weno" + order = getfield_safe(config, :spatial_order, 5) + scheme = "weno$(order)" + end + + if scheme == "eno" + order = getfield_safe(config, :spatial_order, 3) + return Main.EnoReconstructor(order, domain.ntcells) + elseif scheme == "weno3" + return Main.Weno3Reconstructor() + else + error("不支持的重建格式: $scheme(仅支持 eno/weno3)") + end +end + +# 辅助函数(复制自 domain.jl) +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +end # module ReconstructorFactory + +export ReconstructorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02g/reconstructor/weno3.jl b/example/1d-linear-convection/weno3/julia/02g/reconstructor/weno3.jl new file mode 100644 index 00000000..2b6fe1ab --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02g/reconstructor/weno3.jl @@ -0,0 +1,65 @@ +# julia/reconstructor/weno3.jl +""" +WENO3 重构器(与 reconstructor/weno3.py 完全同构) +""" + +mutable struct Weno3Reconstructor + # 无字段,与 Python 一致 +end + +function reconstruct(rec::Weno3Reconstructor, q::Vector{Float64}, cfd::Any) + domain = cfd.domain + solution = cfd.solution + _reconstruct_left_interfaces(domain, q, solution.q_face_left) + _reconstruct_right_interfaces(domain, q, solution.q_face_right) +end + +function _reconstruct_left_interfaces(domain, u, qL) + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in (domain.ist - 1):(domain.ied - 1) + j = i - (domain.ist - 1) + 1 # ← Julia 1-based: j = i - (ist-1) 对应 Python j = i - (ist-1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = _reconstruct_from_left_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_right_interfaces(domain, u, qR) + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in domain.ist:domain.ied + j = i - domain.ist + 1 # ← Julia 1-based: j = i - ist 对应 Python j = i - ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = _reconstruct_from_right_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_from_left_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -0.5*v1 + 1.5*v2 # r=1 stencil + q1 = 0.5*v2 + 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end + +function _reconstruct_from_right_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 0.5*v1 + 0.5*v2 # r=1 stencil + q1 = 1.5*v2 - 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02g/residual.jl b/example/1d-linear-convection/weno3/julia/02g/residual.jl new file mode 100644 index 00000000..11a88593 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02g/residual.jl @@ -0,0 +1,69 @@ +# julia/residual.jl +""" +残差计算器(与 residual.py 完全同构) +- 封装重建→通量→散度完整流程 +- 依赖 cfd 的多个字段 +""" + +include("mesh.jl") + +mutable struct ResidualCalculator + cfd::Any + config::Any + domain::Any + solution::Any + mesh::Mesh + reconstructor::Any + flux_calculator::Any + + function ResidualCalculator(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + mesh = domain.mesh + reconstructor = cfd.reconstructor + + # ✅ 内部创建 flux_calculator(对标 Python) + flux_calculator = FluxCalculatorFactory.create(cfd) + + new(cfd, config, domain, solution, mesh, reconstructor, flux_calculator) + end +end + + +""" +计算完整残差(对外唯一接口) +""" +function compute!(calc::ResidualCalculator) + _reconstruct(calc) + _compute_inviscid_flux(calc) + _compute_flux_divergence(calc) +end + +""" +私有方法:界面值重建 +""" +function _reconstruct(calc::ResidualCalculator) + reconstruct(calc.reconstructor, calc.solution.u, calc.cfd) +end + +""" +私有方法:计算无粘通量 +""" +function _compute_inviscid_flux(calc::ResidualCalculator) + compute!(calc.flux_calculator, + calc.solution.q_face_left, + calc.solution.q_face_right, + calc.solution.flux) +end + +""" +私有方法:计算通量散度(残差 = -dF/dx) +""" +function _compute_flux_divergence(calc::ResidualCalculator) + solution = calc.solution + mesh = calc.mesh + for i in 1:mesh.ncells + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / mesh.dx + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02g/run_eno_weno.jl b/example/1d-linear-convection/weno3/julia/02g/run_eno_weno.jl new file mode 100644 index 00000000..58b6cfa5 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02g/run_eno_weno.jl @@ -0,0 +1,50 @@ +# julia/run_eno_weno.jl +""" +1:1 复刻 run_eno_weno.py 的 Julia 版本 +""" + +include("config.jl") +include("mesh.jl") +include("solver.jl") +include("plotter.jl") + +function performEnoWenoAnalysis() + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + println("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + with_reconstruction(config_eno3, "eno", 3) # 显式指定 3 阶 + config_eno3.dt = 0.0025 # 覆盖默认值 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + run!(cfd_eno3) # 求解并生成 result 字典 + + # 3. 配置并运行 WENO3 求解 + println("Running WENO3 solver...") + config_weno3 = CfdConfig() + with_reconstruction(config_weno3, "weno", 3) # 显式指定 3 阶(WENO 默认 5 阶) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + run!(cfd_weno3) + + # 5. 绘制 ENO/WENO 对比图 + println("Plotting comparison results...") + plot_eno_weno_comparison( + cfd_eno3.result, + cfd_weno3.result; + save_path="eno_weno_comparison.png" + ) + + return cfd_eno3, cfd_weno3 +end + +# 主程序入口 +if abspath(PROGRAM_FILE) == @__FILE__ + performEnoWenoAnalysis() + println("Analysis completed!") +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02g/solution.jl b/example/1d-linear-convection/weno3/julia/02g/solution.jl new file mode 100644 index 00000000..d1f24e68 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02g/solution.jl @@ -0,0 +1,64 @@ +# julia/solution.jl +""" +Solution 结构体(与真实 solution.py 完全同构) +字段顺序、方法、逻辑 1:1 对齐 +""" + +include("mesh.jl") +include("domain.jl") +include("initial_condition.jl") + +mutable struct Solution + domain::Domain + q_face_left::Vector{Float64} + q_face_right::Vector{Float64} + flux::Vector{Float64} + res::Vector{Float64} + u::Vector{Float64} + un::Vector{Float64} + + function Solution(config::Any, domain::Domain) + mesh = domain.mesh + + q_face_left = zeros(Float64, mesh.nnodes) + q_face_right = zeros(Float64, mesh.nnodes) + flux = zeros(Float64, mesh.nnodes) + res = zeros(Float64, mesh.ncells) + u = zeros(Float64, domain.ntcells) + un = zeros(Float64, domain.ntcells) + + sol = new(domain, q_face_left, q_face_right, flux, res, u, un) + initialize_from_config(sol, config) + return sol + end +end + +""" +重置解数组为初始状态 +""" +function reset_solution(sol::Solution) + fill!(sol.u, 0.0) + fill!(sol.un, 0.0) +end + +""" +根据配置初始化场 +""" + +function initialize_from_config(sol::Solution, config::Any) + ic_type = getfield_safe(config, :ic_type, "step") + ic = InitialConditionFactory.create(ic_type, config) + apply(ic, sol) +end + +""" +更新旧场:un = u +""" +function update_old_field(sol::Solution) + sol.un .= sol.u # 等价于 Python 的 un[:] = u[:] +end + +# ---------------------- 辅助函数 ---------------------- +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02g/solver.jl b/example/1d-linear-convection/weno3/julia/02g/solver.jl new file mode 100644 index 00000000..d8193f0e --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02g/solver.jl @@ -0,0 +1,130 @@ +# julia/solver.jl +""" +CFD 求解器主类(与 solver.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("initial_condition.jl") +include("boundary.jl") +include("flux.jl") +include("residual.jl") +include("time_integration.jl") +include("reconstructor/eno.jl") +include("reconstructor/weno3.jl") +include("reconstructor/factory.jl") + +# 导入工厂模块(必须在顶层!) +using .FluxCalculatorFactory +using .TimeIntegratorFactory +using .BoundaryConditionFactory +using .ReconstructorFactory + +# ---------------------- Cfd 求解器 ---------------------- +mutable struct Cfd + config::Any + domain::Domain + solution::Solution + reconstructor::Any + residual_calculator::ResidualCalculator + integrator::Any + boundary_condition::Any + result::Dict{String, Any} + + function Cfd(config::Any, mesh::Mesh) + domain = Domain(config, mesh) + solution = Solution(config, domain) + reconstructor = ReconstructorFactory.create(config, domain) + + # 1. 初始上下文(仅包含不依赖其他组件的字段) + full_cfd = ( + config = config, + domain = domain, + solution = solution, + reconstructor = reconstructor + # 注意:不预先占位 nothing! + ) + + # 2. 创建 boundary_condition(只依赖 config + domain) + boundary_condition = BoundaryConditionFactory.create(full_cfd) + full_cfd = merge(full_cfd, (boundary_condition = boundary_condition,)) + + # 3. 创建 residual_calculator(依赖上面所有字段) + residual_calculator = ResidualCalculator(full_cfd) + full_cfd = merge(full_cfd, (residual_calculator = residual_calculator,)) + + # 4. 创建 integrator(依赖 residual_calculator 等) + integrator = TimeIntegratorFactory.create(full_cfd) + full_cfd = merge(full_cfd, (integrator = integrator,)) + + # 5. 注入完整 self 到组件(确保它们能访问彼此) + residual_calculator.cfd = full_cfd + integrator.base.cfd = full_cfd + + result = Dict{String, Any}() + new(config, domain, solution, reconstructor, residual_calculator, integrator, boundary_condition, result) + end +end + +""" +通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界 +""" +function exact_solution(cfd::Cfd) + x = cfd.domain.mesh.xcc + T = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = @. (x - c * T + L) % L + + # ✅ 使用工厂创建初始条件(对标 Python) + ic = InitialConditionFactory.create(cfd.config.ic_type, cfd.config) + + return evaluate_at(ic, x_shifted) +end + +""" +主求解循环 +""" +function run!(cfd::Cfd) + # 应用初始边界条件并同步 old field + apply!(cfd.boundary_condition, cfd.solution.u) + update_old_field(cfd.solution) + + t = 0.0 + dt_old = cfd.config.dt + dt = dt_old + + while t < cfd.config.final_time + if t + dt > cfd.config.final_time + dt = cfd.config.final_time - t + end + #@show t, dt, maximum(cfd.solution.u), minimum(cfd.solution.u) + # 执行时间步 + step(cfd.integrator, dt) + t += dt + end + + # 恢复 dt + cfd.config.dt = dt_old + + # 整理结果 + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied-1] # Python: [ist:ied] + analytical = exact_solution(cfd) + + cfd.result = Dict( + "x" => cfd.domain.mesh.xcc, + "numerical" => u_numerical, + "analytical" => analytical, + "config" => Dict( + "scheme" => cfd.config.recon_scheme, + "order" => cfd.config.spatial_order, + "rk_order" => cfd.config.rk_order, + "final_time" => cfd.config.final_time + ) + ) + + return u_numerical +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/02g/time_integration.jl b/example/1d-linear-convection/weno3/julia/02g/time_integration.jl new file mode 100644 index 00000000..add564ba --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/02g/time_integration.jl @@ -0,0 +1,169 @@ +# julia/time_integration.jl +""" +时间推进器模块(与 time_integration.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("residual.jl") +include("boundary.jl") + +# ---------------------- 抽象时间推进器基类 ---------------------- +abstract type TimeIntegrator end + +mutable struct TimeIntegratorBase <: TimeIntegrator + cfd::Any + config::Any + domain::Domain + solution::Solution + residual_calculator::Any # ResidualCalculator +end + +function TimeIntegratorBase(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + residual_calculator = cfd.residual_calculator + TimeIntegratorBase(cfd, config, domain, solution, residual_calculator) +end + +function compute_residual(integrator::TimeIntegratorBase) + compute!(integrator.residual_calculator) +end + +function apply_boundary(integrator::TimeIntegratorBase) + apply!(integrator.cfd.boundary_condition, integrator.solution.u) +end + +function map_idx(integrator::TimeIntegratorBase, i::Int) + return i - integrator.domain.ist + 1 # ← +1 转为 1-based +end + +# ---------------------- RK1Integrator ---------------------- +mutable struct RK1Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK1Integrator(cfd::Any) + RK1Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK1Integrator, dt::Float64) + compute_residual(integrator.base) + for i in integrator.base.domain.ist:(integrator.base.domain.ied - 1) + j = map_idx(integrator.base, i) + integrator.base.solution.u[i] += dt * integrator.base.solution.res[j] + end + apply_boundary(integrator.base) + update_old_field(integrator.base.solution) +end + +# ---------------------- RK2Integrator ---------------------- +mutable struct RK2Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK2Integrator(cfd::Any) + RK2Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK2Integrator, dt::Float64) + base = integrator.base + # 阶段1:预测步 + compute_residual(base) + u_pred = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u_pred[i] += dt * base.solution.res[j] + end + base.solution.u .= u_pred + apply_boundary(base) + # 阶段2:校正步 + compute_residual(base) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = 0.5 * base.solution.un[i] + 0.5 * base.solution.u[i] + 0.5 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- RK3Integrator ---------------------- +mutable struct RK3Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK3Integrator(cfd::Any) + RK3Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK3Integrator, dt::Float64) + base = integrator.base + # 阶段1 + compute_residual(base) + u1 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u1[i] += dt * base.solution.res[j] + end + base.solution.u .= u1 + apply_boundary(base) + # 阶段2 + compute_residual(base) + u2 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u2[i] = 0.75 * base.solution.un[i] + 0.25 * base.solution.u[i] + 0.25 * dt * base.solution.res[j] + end + base.solution.u .= u2 + apply_boundary(base) + # 阶段3 + compute_residual(base) + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = c1 * base.solution.un[i] + c2 * base.solution.u[i] + c3 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- TimeIntegratorFactory ---------------------- +module TimeIntegratorFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "时间积分器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + rk_order = cfd.config.rk_order + if !(rk_order isa Integer) || rk_order < 1 + error("rk_order 必须为正整数,当前值: $rk_order") + end + + name = "rk$rk_order" + if !haskey(_REGISTRY, name) + available = sort(collect(keys(_REGISTRY))) + error("未注册的时间积分器: '$name'。可用选项: $available") + end + + return _REGISTRY[name](cfd) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("rk1", cfd -> Main.RK1Integrator(cfd)) +register("rk2", cfd -> Main.RK2Integrator(cfd)) +register("rk3", cfd -> Main.RK3Integrator(cfd)) + +end # module TimeIntegratorFactory + +export TimeIntegratorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03/examples/run_eno_weno.jl b/example/1d-linear-convection/weno3/julia/03/examples/run_eno_weno.jl new file mode 100644 index 00000000..da24e6cd --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03/examples/run_eno_weno.jl @@ -0,0 +1,50 @@ +# examples/run_eno_weno.jl +""" +1:1 复刻 run_eno_weno.py 的 Julia 版本 +""" + +include("../src/config.jl") +include("../src/mesh.jl") +include("../src/solver.jl") +include("../src/plotter.jl") + +function performEnoWenoAnalysis() + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + println("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + with_reconstruction(config_eno3, "eno", 3) # 显式指定 3 阶 + config_eno3.dt = 0.0025 # 覆盖默认值 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + run!(cfd_eno3) # 求解并生成 result 字典 + + # 3. 配置并运行 WENO3 求解 + println("Running WENO3 solver...") + config_weno3 = CfdConfig() + with_reconstruction(config_weno3, "weno", 3) # 显式指定 3 阶(WENO 默认 5 阶) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + run!(cfd_weno3) + + # 5. 绘制 ENO/WENO 对比图 + println("Plotting comparison results...") + plot_eno_weno_comparison( + cfd_eno3.result, + cfd_weno3.result; + save_path="eno_weno_comparison.png" + ) + + return cfd_eno3, cfd_weno3 +end + +# 主程序入口 +if abspath(PROGRAM_FILE) == @__FILE__ + performEnoWenoAnalysis() + println("Analysis completed!") +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03/src/boundary.jl b/example/1d-linear-convection/weno3/julia/03/src/boundary.jl new file mode 100644 index 00000000..82f58b71 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03/src/boundary.jl @@ -0,0 +1,121 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +#using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end + + +# ---------------------- BoundaryConditionFactory ---------------------- +module BoundaryConditionFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "边界条件 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + bc_type = cfd.config.boundary_type + if !(bc_type isa AbstractString) + error("boundary_type 必须为字符串,当前值: $bc_type") + end + + if !haskey(_REGISTRY, bc_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的边界条件: '$bc_type'。可用选项: $available") + end + + return _REGISTRY[bc_type](cfd) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("periodic", cfd -> Main.PeriodicBoundary(cfd)) +register("dirichlet", cfd -> Main.DirichletBoundary(cfd)) +register("neumann", cfd -> Main.NeumannBoundary(cfd)) + +end # module BoundaryConditionFactory + +export BoundaryConditionFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03/src/config.jl b/example/1d-linear-convection/weno3/julia/03/src/config.jl new file mode 100644 index 00000000..4b9494c4 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03/src/config.jl @@ -0,0 +1,76 @@ +# julia/config.jl +""" +CfdConfig:与 Python config.py 完全同构 +""" +mutable struct CfdConfig + ic_type::String + recon_scheme::String + flux_type::String + rk_order::Int + wave_speed::Float64 + final_time::Float64 + dt::Float64 + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + spatial_order::Int + + function CfdConfig() + new( + "step", + "eno", + "rusanov", + 1, + 1.0, + 0.625, + 0.025, + "periodic", + 1.0, + 2.0, + 2 + ) + end +end + +""" +专用配置:重建方案(链式调用) +""" +function with_reconstruction(cfg::CfdConfig, scheme::String, order::Union{Int, Nothing}=nothing) + cfg.recon_scheme = lowercase(scheme) + + if order !== nothing + cfg.spatial_order = order + else + if startswith(cfg.recon_scheme, "weno") + cfg.spatial_order = 5 + elseif cfg.recon_scheme == "eno" + cfg.spatial_order = 3 + else + error("不支持的重建格式:$scheme(仅支持 eno/weno)") + end + end + + return cfg # 支持链式调用 +end + +""" +专用配置:边界条件(链式调用) +""" +function with_boundary(cfg::CfdConfig, bc_type::String; left_value=nothing, right_value=nothing) + cfg.boundary_type = bc_type + if left_value !== nothing + cfg.left_boundary_value = left_value + end + if right_value !== nothing + cfg.right_boundary_value = right_value + end + return cfg +end + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03/src/domain.jl b/example/1d-linear-convection/weno3/julia/03/src/domain.jl new file mode 100644 index 00000000..a06526b8 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03/src/domain.jl @@ -0,0 +1,57 @@ +# julia/domain.jl +""" +Domain 结构体(与 domain.py 完全同构) +- 保存 config +- nghosts 计算逻辑 1:1 +- ist/ied 为 0-based(与 Python 一致) +""" + +include("mesh.jl") + +mutable struct Domain + config::Any # 保存 config(与 Python 一致) + mesh::Mesh + nghosts::Int + ist::Int # 0-based + ied::Int # 0-based + ntcells::Int +end + +function Domain(config::Any, mesh::Mesh) + nghosts = _calc_nghosts(config) + ist = nghosts + 1 # ← 1-based 起始索引 + ied = ist + mesh.ncells # ← 1-based 结束索引(不包含) + ntcells = mesh.ncells + 2 * nghosts + Domain(config, mesh, nghosts, ist, ied, ntcells) +end + +function _calc_nghosts(config::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + order = getfield_safe(config, :spatial_order, 2) + + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + if scheme == "eno" + nghosts = order + elseif startswith(scheme, "weno") + nghosts = order ÷ 2 + 1 + else + error("未知重建格式 $(scheme),无法计算ghost层!") + end + + if nghosts <= 0 + error("计算得到的ghost层数量无效:$(nghosts)(阶数$(order),格式$(scheme))") + end + + return nghosts +end + +function is_physical_cell(domain::Domain, idx::Int) + return domain.ist <= idx < domain.ied +end + +function get_physical_indices(domain::Domain) + return domain.ist:(domain.ied - 1) +end diff --git a/example/1d-linear-convection/weno3/julia/03/src/flux.jl b/example/1d-linear-convection/weno3/julia/03/src/flux.jl new file mode 100644 index 00000000..cff23582 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03/src/flux.jl @@ -0,0 +1,112 @@ +# julia/flux.jl +""" +通量计算器模块(与 flux.py 完全同构) +- 抽象基类 + 具体实现 +- 字段:cfd, config, mesh, wave_speed +""" + +include("mesh.jl") + +# ---------------------- 抽象基类 ---------------------- +""" +InviscidFluxCalculator 抽象类型 +Julia 无 ABC,用文档约定 +所有子类型必须实现 compute! +""" +abstract type InviscidFluxCalculator end + +# ---------------------- RusanovFluxCalculator ---------------------- +mutable struct RusanovFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function RusanovFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::RusanovFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = calc.wave_speed + c_R = calc.wave_speed + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + end +end + +# ---------------------- EngquistOsherFluxCalculator ---------------------- +mutable struct EngquistOsherFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function EngquistOsherFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::EngquistOsherFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + c = calc.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + end +end + +# ---------------------- FluxCalculatorFactory ---------------------- +module FluxCalculatorFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "通量计算器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + config = cfd.config + if !hasproperty(config, :flux_type) + error("cfd.config 缺少 flux_type 字段") + end + + flux_type = config.flux_type + if !(flux_type isa AbstractString) + error("flux_type 必须为字符串,当前值: $flux_type") + end + + if !haskey(_REGISTRY, flux_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的通量计算器: '$flux_type'。可用选项: $available") + end + + return _REGISTRY[flux_type](cfd) +end + +# ✅ 修正:使用 Main. 前缀 +register("rusanov", cfd -> Main.RusanovFluxCalculator(cfd)) +register("engquist-osher", cfd -> Main.EngquistOsherFluxCalculator(cfd)) + +end # module FluxCalculatorFactory + +export FluxCalculatorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03/src/initial_condition.jl b/example/1d-linear-convection/weno3/julia/03/src/initial_condition.jl new file mode 100644 index 00000000..1b9993aa --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03/src/initial_condition.jl @@ -0,0 +1,112 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end + +# ---------------------- InitialConditionFactory ---------------------- +module InitialConditionFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "初始条件 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(ic_type::String, config::Any) + if !(ic_type isa AbstractString) + error("ic_type 必须为字符串,当前值: $ic_type") + end + + if !haskey(_REGISTRY, ic_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的初始条件: '$ic_type'。可用选项: $available") + end + + return _REGISTRY[ic_type](config) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("step", config -> Main.StepFunctionIC(config)) +register("sin", config -> Main.SineWaveIC(config)) +register("gaussian", config -> Main.GaussianPulseIC(config)) + +end # module InitialConditionFactory + +export InitialConditionFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03/src/mesh.jl b/example/1d-linear-convection/weno3/julia/03/src/mesh.jl new file mode 100644 index 00000000..1b0dd4bf --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03/src/mesh.jl @@ -0,0 +1,45 @@ +# julia/mesh.jl +""" +Mesh 结构体(与提供的 mesh.py 完全同构) +- 字段名、初始化顺序、循环逻辑完全一致 +- 不做任何泛化或优化 +""" + +mutable struct Mesh + xmin::Float64 + xmax::Float64 + ncells::Int + nnodes::Int + nx::Int + x::Vector{Float64} + xcc::Vector{Float64} + L::Float64 # 在 init_mesh 中赋值 + dx::Float64 # 在 init_mesh 中赋值 + + function Mesh() + # 与 Python __init__ 完全一致 + xmin = 0.0 + xmax = 2.0 + ncells = 40 + nnodes = ncells + 1 + nx = ncells + x = zeros(Float64, nnodes) + xcc = zeros(Float64, ncells) + + # 调用 init_mesh(模拟 Python) + L = xmax - xmin + dx = L / ncells + + # Generate node coordinates: for i in range(self.nnodes) + for i in 1:nnodes + x[i] = xmin + (i - 1) * dx # Python i → Julia i-1 + end + + # Generate cell center coordinates + for i in 1:ncells + xcc[i] = 0.5 * (x[i] + x[i+1]) + end + + new(xmin, xmax, ncells, nnodes, nx, x, xcc, L, dx) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03/src/plotter.jl b/example/1d-linear-convection/weno3/julia/03/src/plotter.jl new file mode 100644 index 00000000..a77d8d68 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03/src/plotter.jl @@ -0,0 +1,147 @@ +# julia/plotter.jl +""" +CFDPlotter 的 Julia 实现(通过 PythonCall.jl 调用 Matplotlib) +确保与 Python plotter.py 行为完全一致 +""" + +using PythonCall + +# 初始化 Python 环境(加载 matplotlib, inflect) +const plt = pyimport("matplotlib.pyplot") +const inflect = pyimport("inflect") + +mutable struct CFDPlotter + default_styles::Dict{String, Any} + p::Py +end + +function CFDPlotter() + default_styles = Dict{String, Any}( + "numerical" => Dict( + :color => "blue", + :linestyle => "-", + :marker => "o", + :markerfacecolor => "none" + ), + "analytical" => Dict( + :color => "red", + :linestyle => "--", + :marker => "", + :linewidth => 1.5 + ), + "comparison" => [ + Dict(:color => "black", :linestyle => "-", :marker => "o", :markerfacecolor => "none"), + Dict(:color => "blue", :linestyle => "--", :marker => "s", :markerfacecolor => "none"), + Dict(:color => "green", :linestyle => ":", :marker => "^", :markerfacecolor => "none") + ] + ) + p = inflect.engine() + CFDPlotter(default_styles, p) +end + +""" +轻量即时绘图(快速验证结果) +""" +function plot_quick(plotter::CFDPlotter, cfd_result::Dict; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + rk_order = cfd_result["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = cfd_result["config"]["final_time"] + order = cfd_result["config"]["order"] + scheme = uppercase(cfd_result["config"]["scheme"]) + actual_title = "1D Convection (t=$(final_time))\n$(order)th-order $(scheme) + $(rk_str)-order RK" + end + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"]; + label="Numerical ($(uppercase(cfd_result["config"]["scheme"])))", + plotter.default_styles["numerical"]..., + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + # 通用样式 + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +""" +多格式/多精度对比绘图 +""" +function plot_comparison(plotter::CFDPlotter, result_list::Vector{Dict{String, Any}}; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + schemes = [uppercase(r["config"]["scheme"]) * string(r["config"]["order"]) for r in result_list] + rk_order = result_list[1]["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = result_list[1]["config"]["final_time"] + actual_title = "1D Convection Comparison (t=$(final_time))\n$(join(schemes, ", ")) + $(rk_str)-order RK" + end + + # 绘制多个数值解 + for (i, res) in enumerate(result_list) + style = plotter.default_styles["comparison"][mod1(i, length(plotter.default_styles["comparison"]))] + label = "Numerical ($(uppercase(res["config"]["scheme"]))$(res["config"]["order"]))" + plt.plot( + res["x"], res["numerical"]; + label=label, + style..., + markersize=5, linewidth=0.5 + ) + end + + # 绘制解析解 + plt.plot( + result_list[1]["x"], result_list[1]["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +function _set_common_style(plotter::CFDPlotter, title::String) + plt.title(title, fontsize=12) + plt.xlabel("x", fontsize=10) + plt.ylabel("u", fontsize=10) + plt.legend(fontsize=9) + plt.grid(true, color="gray", linestyle="--", linewidth=0.5, alpha=0.7) + plt.tight_layout() +end + +""" +快捷函数:ENO/WENO对比绘图 +""" +function plot_eno_weno_comparison(eno_result::Dict, weno_result::Dict; save_path=nothing) + plotter = CFDPlotter() + plot_comparison(plotter, [eno_result, weno_result]; save_path=save_path) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03/src/reconstructor/eno.jl b/example/1d-linear-convection/weno3/julia/03/src/reconstructor/eno.jl new file mode 100644 index 00000000..e78a636f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03/src/reconstructor/eno.jl @@ -0,0 +1,107 @@ +# julia/reconstructor/eno.jl +""" +ENO 重构器(与 reconstructor/eno.py 完全同构) +""" + +# ---------------------- ENO 系数初始化 ---------------------- +function _init_eno_coef!(spatial_order::Int, coef::Matrix{Float64}) + if spatial_order == 1 + coef[1, 1] = 1.0 + coef[2, 1] = 1.0 + elseif spatial_order == 2 + coef[1, 1:2] = [3.0/2.0, -1.0/2.0] + coef[2, 1:2] = [1.0/2.0, 1.0/2.0] + coef[3, 1:2] = [-1.0/2.0, 3.0/2.0] + elseif spatial_order == 3 + coef[1, 1:3] = [11.0/6.0, -7.0/6.0, 1.0/3.0] + coef[2, 1:3] = [1.0/3.0, 5.0/6.0, -1.0/6.0] + coef[3, 1:3] = [-1.0/6.0, 5.0/6.0, 1.0/3.0] + coef[4, 1:3] = [1.0/3.0, -7.0/6.0, 11.0/6.0] + elseif spatial_order == 4 + coef[1, 1:4] = [25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0] + coef[2, 1:4] = [1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0] + coef[3, 1:4] = [-1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0] + coef[4, 1:4] = [1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0] + coef[5, 1:4] = [-1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0] + elseif spatial_order == 5 + coef[1, 1:5] = [137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0] + coef[2, 1:5] = [1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0] + coef[3, 1:5] = [-1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0] + coef[4, 1:5] = [1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0] + coef[5, 1:5] = [-1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0] + coef[6, 1:5] = [1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0] + elseif spatial_order == 6 + coef[1, 1:6] = [49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0] + coef[2, 1:6] = [1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0] + coef[3, 1:6] = [-1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0] + coef[4, 1:6] = [1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0] + coef[5, 1:6] = [-1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0] + coef[6, 1:6] = [1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0] + coef[7, 1:6] = [-1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0] + elseif spatial_order == 7 + coef[1, 1:7] = [363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0] + coef[2, 1:7] = [1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0] + coef[3, 1:7] = [-1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0] + coef[4, 1:7] = [1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0] + coef[5, 1:7] = [-1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0] + coef[6, 1:7] = [1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0] + coef[7, 1:7] = [-1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0] + coef[8, 1:7] = [1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0] + else + error("ENO 系数未实现 order=$spatial_order") + end +end + +# ---------------------- ENO 重构器 ---------------------- +mutable struct EnoReconstructor + spatial_order::Int + ntcells::Int + lmc::Vector{Int} + coef::Matrix{Float64} + dd::Matrix{Float64} + + function EnoReconstructor(spatial_order::Int, ntcells::Int) + lmc = zeros(Int, ntcells) + coef = zeros(Float64, spatial_order + 1, spatial_order) + dd = zeros(Float64, spatial_order, ntcells) + _init_eno_coef!(spatial_order, coef) + new(spatial_order, ntcells, lmc, coef, dd) + end +end + +function reconstruct(rec::EnoReconstructor, q::Vector{Float64}, cfd::Any) + # 1. 差商计算 (dd[1,:] = q) + @views rec.dd[1, :] .= q + for m in 2:rec.spatial_order + for j in 1:(rec.ntcells - m + 1) + rec.dd[m, j] = rec.dd[m-1, j+1] - rec.dd[m-1, j] + end + end + + # 2. 选择 smoothest stencil + domain = cfd.domain + for i in (domain.ist - 1):(domain.ied) # Python: range(ist-1, ied+1) → ied+1-1 = ied + rec.lmc[i] = i + for m in 2:rec.spatial_order + if abs(rec.dd[m, rec.lmc[i] - 1]) < abs(rec.dd[m, rec.lmc[i]]) + rec.lmc[i] -= 1 + end + end + end + + # 3. 重构界面值 + solution = cfd.solution + for i in domain.ist:(domain.ied) # Python: range(ist, ied+1) → ied+1-1 = ied + j = i - domain.ist + 1 # Julia 1-based + k1 = rec.lmc[i - 1] + k2 = rec.lmc[i] + r1 = (i - 1) - k1 + 1 + r2 = i - k2 + 1 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in 1:rec.spatial_order + solution.q_face_left[j] += q[k1 + m - 1] * rec.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m - 1] * rec.coef[r2, m] + end + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03/src/reconstructor/factory.jl b/example/1d-linear-convection/weno3/julia/03/src/reconstructor/factory.jl new file mode 100644 index 00000000..ac1ea1c8 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03/src/reconstructor/factory.jl @@ -0,0 +1,41 @@ +# julia/reconstructor/factory.jl + +""" +ReconstructorFactory +对标 Python: ReconstructorFactory.create(config, domain) +""" +module ReconstructorFactory + +# ✅ 不要用 using ..EnoReconstructor(它们不是模块) +# 直接通过 Main. 引用顶层定义的类型 + +function create(config::Any, domain::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + # 处理 WENO 默认命名(如 Python) + if scheme == "weno" + order = getfield_safe(config, :spatial_order, 5) + scheme = "weno$(order)" + end + + if scheme == "eno" + order = getfield_safe(config, :spatial_order, 3) + return Main.EnoReconstructor(order, domain.ntcells) + elseif scheme == "weno3" + return Main.Weno3Reconstructor() + else + error("不支持的重建格式: $scheme(仅支持 eno/weno3)") + end +end + +# ---------------------- 辅助函数 ---------------------- +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +end # module ReconstructorFactory + +export ReconstructorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03/src/reconstructor/weno3.jl b/example/1d-linear-convection/weno3/julia/03/src/reconstructor/weno3.jl new file mode 100644 index 00000000..2b6fe1ab --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03/src/reconstructor/weno3.jl @@ -0,0 +1,65 @@ +# julia/reconstructor/weno3.jl +""" +WENO3 重构器(与 reconstructor/weno3.py 完全同构) +""" + +mutable struct Weno3Reconstructor + # 无字段,与 Python 一致 +end + +function reconstruct(rec::Weno3Reconstructor, q::Vector{Float64}, cfd::Any) + domain = cfd.domain + solution = cfd.solution + _reconstruct_left_interfaces(domain, q, solution.q_face_left) + _reconstruct_right_interfaces(domain, q, solution.q_face_right) +end + +function _reconstruct_left_interfaces(domain, u, qL) + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in (domain.ist - 1):(domain.ied - 1) + j = i - (domain.ist - 1) + 1 # ← Julia 1-based: j = i - (ist-1) 对应 Python j = i - (ist-1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = _reconstruct_from_left_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_right_interfaces(domain, u, qR) + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in domain.ist:domain.ied + j = i - domain.ist + 1 # ← Julia 1-based: j = i - ist 对应 Python j = i - ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = _reconstruct_from_right_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_from_left_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -0.5*v1 + 1.5*v2 # r=1 stencil + q1 = 0.5*v2 + 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end + +function _reconstruct_from_right_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 0.5*v1 + 0.5*v2 # r=1 stencil + q1 = 1.5*v2 - 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03/src/registry.jl b/example/1d-linear-convection/weno3/julia/03/src/registry.jl new file mode 100644 index 00000000..6acd9aaa --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03/src/registry.jl @@ -0,0 +1,164 @@ +""" +统一注册系统 + 通用工厂 +- ComponentRegistry: 组件注册表 +- @register_component: 装饰器宏 +- BaseFactory: 通用工厂接口 +""" + +module ComponentRegistry + +# 注册表:Dict{category => Dict{name => constructor}} +const _REGISTRIES = Dict{String, Dict{String, Function}}() +const _VERBOSE = Ref(true) + +# ---------------------- 注册核心逻辑 ---------------------- +""" + register(category::String, name::String, ctor::Function) + +注册一个组件构造函数到指定类别。 +如果已存在同名组件且不同,则发出警告。 +""" +function register(category::String, name::String, ctor::Function) + if !haskey(_REGISTRIES, category) + _REGISTRIES[category] = Dict{String, Function}() + end + + registry = _REGISTRIES[category] + if haskey(registry, name) + if registry[name] !== ctor && _VERBOSE[] + @warn "覆盖注册: $category.$name" + end + end + + registry[name] = ctor + if _VERBOSE[] + println("✅ 已注册: $category.$name -> $(nameof(ctor))") + end +end + +""" + create(category::String, name::String, args...; kwargs...) + +从注册表创建组件实例。 +""" +function create(category::String, name::String, args...; kwargs...) + if !haskey(_REGISTRIES, category) + error("❌ 未知类别: $category (可用: $(collect(keys(_REGISTRIES))))") + end + + registry = _REGISTRIES[category] + lname = lowercase(name) + if !haskey(registry, lname) + available = sort(collect(keys(registry))) + error("❌ 未找到: $category.$name (可用: $available)") + end + + return registry[lname](args...; kwargs...) +end + +""" + list_all() + +返回所有已注册组件(按类别)。 +""" +function list_all() + return Dict(cat => sort(collect(keys(reg))) for (cat, reg) in _REGISTRIES) +end + +""" + set_verbose(flag::Bool) + +开启/关闭注册提示。 +""" +function set_verbose(flag::Bool) + _VERBOSE[] = flag +end + +end # module ComponentRegistry + + +# ---------------------- 装饰器宏:@register_component ---------------------- +""" +@register_component(category, [name]) + +用法: + @register_component("boundary", "periodic") + struct PeriodicBoundary ... + +若省略 name,则使用类型名的小写形式。 +""" +macro register_component(category::String, name_expr) + error("@register_component 需在类型定义前使用,且必须在模块顶层") +end + +macro register_component(category::String) + error("@register_component(category, name) 需指定 name 或在类型后使用") +end + +# 重载:@register_component("category", "name") struct X ... end +macro register_component(category::String, name::String, ex) + if !Meta.isexpr(ex, :struct) + error("@register_component 必须作用于 struct 定义") + end + + struct_name = ex.args[2] + if Meta.isexpr(struct_name, :curly) + struct_name = struct_name.args[1] + end + + # 插入注册调用(在模块顶层) + quote + $(esc(ex)) + $(ComponentRegistry).register($(category), $(name), $(esc(struct_name))) + end +end + +# 重载:@register_component("category") struct X ... end → name = lowercase(nameof(X)) +macro register_component(category::String, ex) + if !Meta.isexpr(ex, :struct) + error("@register_component 必须作用于 struct 定义") + end + + struct_name = ex.args[2] + if Meta.isexpr(struct_name, :curly) + struct_name = struct_name.args[1] + end + + name_str = string(struct_name) |> lowercase + + quote + $(esc(ex)) + $(ComponentRegistry).register($(category), $(name_str), $(esc(struct_name))) + end +end + + +# ---------------------- 通用工厂 ---------------------- +module BaseFactory + +using ..ComponentRegistry + +""" + create_component(category::String, name::String, args...; kwargs...) + +通用工厂接口,与 Python BaseFactory.create_component 行为一致。 +""" +function create_component(category::String, name::String, args...; kwargs...) + return ComponentRegistry.create(category, name, args...; kwargs...) +end + +""" + get_available_components(category::String) + +列出某类别下所有可用组件。 +""" +function get_available_components(category::String) + all = ComponentRegistry.list_all() + return get(all, category, String[]) +end + +end # module BaseFactory + + +# 导出接口 +export ComponentRegistry, BaseFactory, @register_component \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03/src/residual.jl b/example/1d-linear-convection/weno3/julia/03/src/residual.jl new file mode 100644 index 00000000..11a88593 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03/src/residual.jl @@ -0,0 +1,69 @@ +# julia/residual.jl +""" +残差计算器(与 residual.py 完全同构) +- 封装重建→通量→散度完整流程 +- 依赖 cfd 的多个字段 +""" + +include("mesh.jl") + +mutable struct ResidualCalculator + cfd::Any + config::Any + domain::Any + solution::Any + mesh::Mesh + reconstructor::Any + flux_calculator::Any + + function ResidualCalculator(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + mesh = domain.mesh + reconstructor = cfd.reconstructor + + # ✅ 内部创建 flux_calculator(对标 Python) + flux_calculator = FluxCalculatorFactory.create(cfd) + + new(cfd, config, domain, solution, mesh, reconstructor, flux_calculator) + end +end + + +""" +计算完整残差(对外唯一接口) +""" +function compute!(calc::ResidualCalculator) + _reconstruct(calc) + _compute_inviscid_flux(calc) + _compute_flux_divergence(calc) +end + +""" +私有方法:界面值重建 +""" +function _reconstruct(calc::ResidualCalculator) + reconstruct(calc.reconstructor, calc.solution.u, calc.cfd) +end + +""" +私有方法:计算无粘通量 +""" +function _compute_inviscid_flux(calc::ResidualCalculator) + compute!(calc.flux_calculator, + calc.solution.q_face_left, + calc.solution.q_face_right, + calc.solution.flux) +end + +""" +私有方法:计算通量散度(残差 = -dF/dx) +""" +function _compute_flux_divergence(calc::ResidualCalculator) + solution = calc.solution + mesh = calc.mesh + for i in 1:mesh.ncells + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / mesh.dx + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03/src/solution.jl b/example/1d-linear-convection/weno3/julia/03/src/solution.jl new file mode 100644 index 00000000..7044c001 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03/src/solution.jl @@ -0,0 +1,60 @@ +# julia/solution.jl +""" +Solution 结构体(与真实 solution.py 完全同构) +字段顺序、方法、逻辑 1:1 对齐 +""" + +include("mesh.jl") +include("domain.jl") +include("initial_condition.jl") + +mutable struct Solution + domain::Domain + q_face_left::Vector{Float64} + q_face_right::Vector{Float64} + flux::Vector{Float64} + res::Vector{Float64} + u::Vector{Float64} + un::Vector{Float64} + + function Solution(config::Any, domain::Domain) + mesh = domain.mesh + + q_face_left = zeros(Float64, mesh.nnodes) + q_face_right = zeros(Float64, mesh.nnodes) + flux = zeros(Float64, mesh.nnodes) + res = zeros(Float64, mesh.ncells) + u = zeros(Float64, domain.ntcells) + un = zeros(Float64, domain.ntcells) + + sol = new(domain, q_face_left, q_face_right, flux, res, u, un) + initialize_from_config(sol, config) + return sol + end +end + +""" +重置解数组为初始状态 +""" +function reset_solution(sol::Solution) + fill!(sol.u, 0.0) + fill!(sol.un, 0.0) +end + +""" +根据配置初始化场 +""" + +function initialize_from_config(sol::Solution, config::Any) + ic_type = getfield_safe(config, :ic_type, "step") + ic = InitialConditionFactory.create(ic_type, config) + apply(ic, sol) +end + +""" +更新旧场:un = u +""" +function update_old_field(sol::Solution) + sol.un .= sol.u # 等价于 Python 的 un[:] = u[:] +end + diff --git a/example/1d-linear-convection/weno3/julia/03/src/solver.jl b/example/1d-linear-convection/weno3/julia/03/src/solver.jl new file mode 100644 index 00000000..d8193f0e --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03/src/solver.jl @@ -0,0 +1,130 @@ +# julia/solver.jl +""" +CFD 求解器主类(与 solver.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("initial_condition.jl") +include("boundary.jl") +include("flux.jl") +include("residual.jl") +include("time_integration.jl") +include("reconstructor/eno.jl") +include("reconstructor/weno3.jl") +include("reconstructor/factory.jl") + +# 导入工厂模块(必须在顶层!) +using .FluxCalculatorFactory +using .TimeIntegratorFactory +using .BoundaryConditionFactory +using .ReconstructorFactory + +# ---------------------- Cfd 求解器 ---------------------- +mutable struct Cfd + config::Any + domain::Domain + solution::Solution + reconstructor::Any + residual_calculator::ResidualCalculator + integrator::Any + boundary_condition::Any + result::Dict{String, Any} + + function Cfd(config::Any, mesh::Mesh) + domain = Domain(config, mesh) + solution = Solution(config, domain) + reconstructor = ReconstructorFactory.create(config, domain) + + # 1. 初始上下文(仅包含不依赖其他组件的字段) + full_cfd = ( + config = config, + domain = domain, + solution = solution, + reconstructor = reconstructor + # 注意:不预先占位 nothing! + ) + + # 2. 创建 boundary_condition(只依赖 config + domain) + boundary_condition = BoundaryConditionFactory.create(full_cfd) + full_cfd = merge(full_cfd, (boundary_condition = boundary_condition,)) + + # 3. 创建 residual_calculator(依赖上面所有字段) + residual_calculator = ResidualCalculator(full_cfd) + full_cfd = merge(full_cfd, (residual_calculator = residual_calculator,)) + + # 4. 创建 integrator(依赖 residual_calculator 等) + integrator = TimeIntegratorFactory.create(full_cfd) + full_cfd = merge(full_cfd, (integrator = integrator,)) + + # 5. 注入完整 self 到组件(确保它们能访问彼此) + residual_calculator.cfd = full_cfd + integrator.base.cfd = full_cfd + + result = Dict{String, Any}() + new(config, domain, solution, reconstructor, residual_calculator, integrator, boundary_condition, result) + end +end + +""" +通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界 +""" +function exact_solution(cfd::Cfd) + x = cfd.domain.mesh.xcc + T = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = @. (x - c * T + L) % L + + # ✅ 使用工厂创建初始条件(对标 Python) + ic = InitialConditionFactory.create(cfd.config.ic_type, cfd.config) + + return evaluate_at(ic, x_shifted) +end + +""" +主求解循环 +""" +function run!(cfd::Cfd) + # 应用初始边界条件并同步 old field + apply!(cfd.boundary_condition, cfd.solution.u) + update_old_field(cfd.solution) + + t = 0.0 + dt_old = cfd.config.dt + dt = dt_old + + while t < cfd.config.final_time + if t + dt > cfd.config.final_time + dt = cfd.config.final_time - t + end + #@show t, dt, maximum(cfd.solution.u), minimum(cfd.solution.u) + # 执行时间步 + step(cfd.integrator, dt) + t += dt + end + + # 恢复 dt + cfd.config.dt = dt_old + + # 整理结果 + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied-1] # Python: [ist:ied] + analytical = exact_solution(cfd) + + cfd.result = Dict( + "x" => cfd.domain.mesh.xcc, + "numerical" => u_numerical, + "analytical" => analytical, + "config" => Dict( + "scheme" => cfd.config.recon_scheme, + "order" => cfd.config.spatial_order, + "rk_order" => cfd.config.rk_order, + "final_time" => cfd.config.final_time + ) + ) + + return u_numerical +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03/src/time_integration.jl b/example/1d-linear-convection/weno3/julia/03/src/time_integration.jl new file mode 100644 index 00000000..add564ba --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03/src/time_integration.jl @@ -0,0 +1,169 @@ +# julia/time_integration.jl +""" +时间推进器模块(与 time_integration.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("residual.jl") +include("boundary.jl") + +# ---------------------- 抽象时间推进器基类 ---------------------- +abstract type TimeIntegrator end + +mutable struct TimeIntegratorBase <: TimeIntegrator + cfd::Any + config::Any + domain::Domain + solution::Solution + residual_calculator::Any # ResidualCalculator +end + +function TimeIntegratorBase(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + residual_calculator = cfd.residual_calculator + TimeIntegratorBase(cfd, config, domain, solution, residual_calculator) +end + +function compute_residual(integrator::TimeIntegratorBase) + compute!(integrator.residual_calculator) +end + +function apply_boundary(integrator::TimeIntegratorBase) + apply!(integrator.cfd.boundary_condition, integrator.solution.u) +end + +function map_idx(integrator::TimeIntegratorBase, i::Int) + return i - integrator.domain.ist + 1 # ← +1 转为 1-based +end + +# ---------------------- RK1Integrator ---------------------- +mutable struct RK1Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK1Integrator(cfd::Any) + RK1Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK1Integrator, dt::Float64) + compute_residual(integrator.base) + for i in integrator.base.domain.ist:(integrator.base.domain.ied - 1) + j = map_idx(integrator.base, i) + integrator.base.solution.u[i] += dt * integrator.base.solution.res[j] + end + apply_boundary(integrator.base) + update_old_field(integrator.base.solution) +end + +# ---------------------- RK2Integrator ---------------------- +mutable struct RK2Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK2Integrator(cfd::Any) + RK2Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK2Integrator, dt::Float64) + base = integrator.base + # 阶段1:预测步 + compute_residual(base) + u_pred = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u_pred[i] += dt * base.solution.res[j] + end + base.solution.u .= u_pred + apply_boundary(base) + # 阶段2:校正步 + compute_residual(base) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = 0.5 * base.solution.un[i] + 0.5 * base.solution.u[i] + 0.5 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- RK3Integrator ---------------------- +mutable struct RK3Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK3Integrator(cfd::Any) + RK3Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK3Integrator, dt::Float64) + base = integrator.base + # 阶段1 + compute_residual(base) + u1 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u1[i] += dt * base.solution.res[j] + end + base.solution.u .= u1 + apply_boundary(base) + # 阶段2 + compute_residual(base) + u2 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u2[i] = 0.75 * base.solution.un[i] + 0.25 * base.solution.u[i] + 0.25 * dt * base.solution.res[j] + end + base.solution.u .= u2 + apply_boundary(base) + # 阶段3 + compute_residual(base) + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = c1 * base.solution.un[i] + c2 * base.solution.u[i] + c3 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- TimeIntegratorFactory ---------------------- +module TimeIntegratorFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "时间积分器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + rk_order = cfd.config.rk_order + if !(rk_order isa Integer) || rk_order < 1 + error("rk_order 必须为正整数,当前值: $rk_order") + end + + name = "rk$rk_order" + if !haskey(_REGISTRY, name) + available = sort(collect(keys(_REGISTRY))) + error("未注册的时间积分器: '$name'。可用选项: $available") + end + + return _REGISTRY[name](cfd) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("rk1", cfd -> Main.RK1Integrator(cfd)) +register("rk2", cfd -> Main.RK2Integrator(cfd)) +register("rk3", cfd -> Main.RK3Integrator(cfd)) + +end # module TimeIntegratorFactory + +export TimeIntegratorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03a/examples/run_eno_weno.jl b/example/1d-linear-convection/weno3/julia/03a/examples/run_eno_weno.jl new file mode 100644 index 00000000..da24e6cd --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03a/examples/run_eno_weno.jl @@ -0,0 +1,50 @@ +# examples/run_eno_weno.jl +""" +1:1 复刻 run_eno_weno.py 的 Julia 版本 +""" + +include("../src/config.jl") +include("../src/mesh.jl") +include("../src/solver.jl") +include("../src/plotter.jl") + +function performEnoWenoAnalysis() + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + println("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + with_reconstruction(config_eno3, "eno", 3) # 显式指定 3 阶 + config_eno3.dt = 0.0025 # 覆盖默认值 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + run!(cfd_eno3) # 求解并生成 result 字典 + + # 3. 配置并运行 WENO3 求解 + println("Running WENO3 solver...") + config_weno3 = CfdConfig() + with_reconstruction(config_weno3, "weno", 3) # 显式指定 3 阶(WENO 默认 5 阶) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + run!(cfd_weno3) + + # 5. 绘制 ENO/WENO 对比图 + println("Plotting comparison results...") + plot_eno_weno_comparison( + cfd_eno3.result, + cfd_weno3.result; + save_path="eno_weno_comparison.png" + ) + + return cfd_eno3, cfd_weno3 +end + +# 主程序入口 +if abspath(PROGRAM_FILE) == @__FILE__ + performEnoWenoAnalysis() + println("Analysis completed!") +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03a/src/boundary.jl b/example/1d-linear-convection/weno3/julia/03a/src/boundary.jl new file mode 100644 index 00000000..82f58b71 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03a/src/boundary.jl @@ -0,0 +1,121 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +#using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end + + +# ---------------------- BoundaryConditionFactory ---------------------- +module BoundaryConditionFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "边界条件 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + bc_type = cfd.config.boundary_type + if !(bc_type isa AbstractString) + error("boundary_type 必须为字符串,当前值: $bc_type") + end + + if !haskey(_REGISTRY, bc_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的边界条件: '$bc_type'。可用选项: $available") + end + + return _REGISTRY[bc_type](cfd) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("periodic", cfd -> Main.PeriodicBoundary(cfd)) +register("dirichlet", cfd -> Main.DirichletBoundary(cfd)) +register("neumann", cfd -> Main.NeumannBoundary(cfd)) + +end # module BoundaryConditionFactory + +export BoundaryConditionFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03a/src/config.jl b/example/1d-linear-convection/weno3/julia/03a/src/config.jl new file mode 100644 index 00000000..dd34fe7a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03a/src/config.jl @@ -0,0 +1,69 @@ +# julia/config.jl +""" +CfdConfig:与 Python config.py 完全同构 +""" +mutable struct CfdConfig + ic_type::String + recon_scheme::String + flux_type::String + rk_order::Int + wave_speed::Float64 + final_time::Float64 + dt::Float64 + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + spatial_order::Int + + function CfdConfig() + new( + "step", + "eno", + "rusanov", + 1, + 1.0, + 0.625, + 0.025, + "periodic", + 1.0, + 2.0, + 2 + ) + end +end + +""" +专用配置:重建方案(链式调用) +""" +function with_reconstruction(cfg::CfdConfig, scheme::String, order::Union{Int, Nothing}=nothing) + cfg.recon_scheme = lowercase(scheme) + + if order !== nothing + cfg.spatial_order = order + else + if startswith(cfg.recon_scheme, "weno") + cfg.spatial_order = 5 + elseif cfg.recon_scheme == "eno" + cfg.spatial_order = 3 + else + error("不支持的重建格式:$scheme(仅支持 eno/weno)") + end + end + + return cfg # 支持链式调用 +end + +""" +专用配置:边界条件(链式调用) +""" +function with_boundary(cfg::CfdConfig, bc_type::String; left_value=nothing, right_value=nothing) + cfg.boundary_type = bc_type + if left_value !== nothing + cfg.left_boundary_value = left_value + end + if right_value !== nothing + cfg.right_boundary_value = right_value + end + return cfg +end + diff --git a/example/1d-linear-convection/weno3/julia/03a/src/domain.jl b/example/1d-linear-convection/weno3/julia/03a/src/domain.jl new file mode 100644 index 00000000..eec26114 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03a/src/domain.jl @@ -0,0 +1,58 @@ +# julia/domain.jl +""" +Domain 结构体(与 domain.py 完全同构) +- 保存 config +- nghosts 计算逻辑 1:1 +- ist/ied 为 0-based(与 Python 一致) +""" + +include("mesh.jl") +include("utils.jl") + +mutable struct Domain + config::Any # 保存 config(与 Python 一致) + mesh::Mesh + nghosts::Int + ist::Int # 0-based + ied::Int # 0-based + ntcells::Int +end + +function Domain(config::Any, mesh::Mesh) + nghosts = _calc_nghosts(config) + ist = nghosts + 1 # ← 1-based 起始索引 + ied = ist + mesh.ncells # ← 1-based 结束索引(不包含) + ntcells = mesh.ncells + 2 * nghosts + Domain(config, mesh, nghosts, ist, ied, ntcells) +end + +function _calc_nghosts(config::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + order = getfield_safe(config, :spatial_order, 2) + + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + if scheme == "eno" + nghosts = order + elseif startswith(scheme, "weno") + nghosts = order ÷ 2 + 1 + else + error("未知重建格式 $(scheme),无法计算ghost层!") + end + + if nghosts <= 0 + error("计算得到的ghost层数量无效:$(nghosts)(阶数$(order),格式$(scheme))") + end + + return nghosts +end + +function is_physical_cell(domain::Domain, idx::Int) + return domain.ist <= idx < domain.ied +end + +function get_physical_indices(domain::Domain) + return domain.ist:(domain.ied - 1) +end diff --git a/example/1d-linear-convection/weno3/julia/03a/src/flux.jl b/example/1d-linear-convection/weno3/julia/03a/src/flux.jl new file mode 100644 index 00000000..cff23582 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03a/src/flux.jl @@ -0,0 +1,112 @@ +# julia/flux.jl +""" +通量计算器模块(与 flux.py 完全同构) +- 抽象基类 + 具体实现 +- 字段:cfd, config, mesh, wave_speed +""" + +include("mesh.jl") + +# ---------------------- 抽象基类 ---------------------- +""" +InviscidFluxCalculator 抽象类型 +Julia 无 ABC,用文档约定 +所有子类型必须实现 compute! +""" +abstract type InviscidFluxCalculator end + +# ---------------------- RusanovFluxCalculator ---------------------- +mutable struct RusanovFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function RusanovFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::RusanovFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = calc.wave_speed + c_R = calc.wave_speed + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + end +end + +# ---------------------- EngquistOsherFluxCalculator ---------------------- +mutable struct EngquistOsherFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function EngquistOsherFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::EngquistOsherFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + c = calc.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + end +end + +# ---------------------- FluxCalculatorFactory ---------------------- +module FluxCalculatorFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "通量计算器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + config = cfd.config + if !hasproperty(config, :flux_type) + error("cfd.config 缺少 flux_type 字段") + end + + flux_type = config.flux_type + if !(flux_type isa AbstractString) + error("flux_type 必须为字符串,当前值: $flux_type") + end + + if !haskey(_REGISTRY, flux_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的通量计算器: '$flux_type'。可用选项: $available") + end + + return _REGISTRY[flux_type](cfd) +end + +# ✅ 修正:使用 Main. 前缀 +register("rusanov", cfd -> Main.RusanovFluxCalculator(cfd)) +register("engquist-osher", cfd -> Main.EngquistOsherFluxCalculator(cfd)) + +end # module FluxCalculatorFactory + +export FluxCalculatorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03a/src/initial_condition.jl b/example/1d-linear-convection/weno3/julia/03a/src/initial_condition.jl new file mode 100644 index 00000000..1b9993aa --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03a/src/initial_condition.jl @@ -0,0 +1,112 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end + +# ---------------------- InitialConditionFactory ---------------------- +module InitialConditionFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "初始条件 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(ic_type::String, config::Any) + if !(ic_type isa AbstractString) + error("ic_type 必须为字符串,当前值: $ic_type") + end + + if !haskey(_REGISTRY, ic_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的初始条件: '$ic_type'。可用选项: $available") + end + + return _REGISTRY[ic_type](config) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("step", config -> Main.StepFunctionIC(config)) +register("sin", config -> Main.SineWaveIC(config)) +register("gaussian", config -> Main.GaussianPulseIC(config)) + +end # module InitialConditionFactory + +export InitialConditionFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03a/src/mesh.jl b/example/1d-linear-convection/weno3/julia/03a/src/mesh.jl new file mode 100644 index 00000000..1b0dd4bf --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03a/src/mesh.jl @@ -0,0 +1,45 @@ +# julia/mesh.jl +""" +Mesh 结构体(与提供的 mesh.py 完全同构) +- 字段名、初始化顺序、循环逻辑完全一致 +- 不做任何泛化或优化 +""" + +mutable struct Mesh + xmin::Float64 + xmax::Float64 + ncells::Int + nnodes::Int + nx::Int + x::Vector{Float64} + xcc::Vector{Float64} + L::Float64 # 在 init_mesh 中赋值 + dx::Float64 # 在 init_mesh 中赋值 + + function Mesh() + # 与 Python __init__ 完全一致 + xmin = 0.0 + xmax = 2.0 + ncells = 40 + nnodes = ncells + 1 + nx = ncells + x = zeros(Float64, nnodes) + xcc = zeros(Float64, ncells) + + # 调用 init_mesh(模拟 Python) + L = xmax - xmin + dx = L / ncells + + # Generate node coordinates: for i in range(self.nnodes) + for i in 1:nnodes + x[i] = xmin + (i - 1) * dx # Python i → Julia i-1 + end + + # Generate cell center coordinates + for i in 1:ncells + xcc[i] = 0.5 * (x[i] + x[i+1]) + end + + new(xmin, xmax, ncells, nnodes, nx, x, xcc, L, dx) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03a/src/plotter.jl b/example/1d-linear-convection/weno3/julia/03a/src/plotter.jl new file mode 100644 index 00000000..a77d8d68 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03a/src/plotter.jl @@ -0,0 +1,147 @@ +# julia/plotter.jl +""" +CFDPlotter 的 Julia 实现(通过 PythonCall.jl 调用 Matplotlib) +确保与 Python plotter.py 行为完全一致 +""" + +using PythonCall + +# 初始化 Python 环境(加载 matplotlib, inflect) +const plt = pyimport("matplotlib.pyplot") +const inflect = pyimport("inflect") + +mutable struct CFDPlotter + default_styles::Dict{String, Any} + p::Py +end + +function CFDPlotter() + default_styles = Dict{String, Any}( + "numerical" => Dict( + :color => "blue", + :linestyle => "-", + :marker => "o", + :markerfacecolor => "none" + ), + "analytical" => Dict( + :color => "red", + :linestyle => "--", + :marker => "", + :linewidth => 1.5 + ), + "comparison" => [ + Dict(:color => "black", :linestyle => "-", :marker => "o", :markerfacecolor => "none"), + Dict(:color => "blue", :linestyle => "--", :marker => "s", :markerfacecolor => "none"), + Dict(:color => "green", :linestyle => ":", :marker => "^", :markerfacecolor => "none") + ] + ) + p = inflect.engine() + CFDPlotter(default_styles, p) +end + +""" +轻量即时绘图(快速验证结果) +""" +function plot_quick(plotter::CFDPlotter, cfd_result::Dict; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + rk_order = cfd_result["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = cfd_result["config"]["final_time"] + order = cfd_result["config"]["order"] + scheme = uppercase(cfd_result["config"]["scheme"]) + actual_title = "1D Convection (t=$(final_time))\n$(order)th-order $(scheme) + $(rk_str)-order RK" + end + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"]; + label="Numerical ($(uppercase(cfd_result["config"]["scheme"])))", + plotter.default_styles["numerical"]..., + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + # 通用样式 + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +""" +多格式/多精度对比绘图 +""" +function plot_comparison(plotter::CFDPlotter, result_list::Vector{Dict{String, Any}}; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + schemes = [uppercase(r["config"]["scheme"]) * string(r["config"]["order"]) for r in result_list] + rk_order = result_list[1]["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = result_list[1]["config"]["final_time"] + actual_title = "1D Convection Comparison (t=$(final_time))\n$(join(schemes, ", ")) + $(rk_str)-order RK" + end + + # 绘制多个数值解 + for (i, res) in enumerate(result_list) + style = plotter.default_styles["comparison"][mod1(i, length(plotter.default_styles["comparison"]))] + label = "Numerical ($(uppercase(res["config"]["scheme"]))$(res["config"]["order"]))" + plt.plot( + res["x"], res["numerical"]; + label=label, + style..., + markersize=5, linewidth=0.5 + ) + end + + # 绘制解析解 + plt.plot( + result_list[1]["x"], result_list[1]["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +function _set_common_style(plotter::CFDPlotter, title::String) + plt.title(title, fontsize=12) + plt.xlabel("x", fontsize=10) + plt.ylabel("u", fontsize=10) + plt.legend(fontsize=9) + plt.grid(true, color="gray", linestyle="--", linewidth=0.5, alpha=0.7) + plt.tight_layout() +end + +""" +快捷函数:ENO/WENO对比绘图 +""" +function plot_eno_weno_comparison(eno_result::Dict, weno_result::Dict; save_path=nothing) + plotter = CFDPlotter() + plot_comparison(plotter, [eno_result, weno_result]; save_path=save_path) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03a/src/reconstructor/eno.jl b/example/1d-linear-convection/weno3/julia/03a/src/reconstructor/eno.jl new file mode 100644 index 00000000..e78a636f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03a/src/reconstructor/eno.jl @@ -0,0 +1,107 @@ +# julia/reconstructor/eno.jl +""" +ENO 重构器(与 reconstructor/eno.py 完全同构) +""" + +# ---------------------- ENO 系数初始化 ---------------------- +function _init_eno_coef!(spatial_order::Int, coef::Matrix{Float64}) + if spatial_order == 1 + coef[1, 1] = 1.0 + coef[2, 1] = 1.0 + elseif spatial_order == 2 + coef[1, 1:2] = [3.0/2.0, -1.0/2.0] + coef[2, 1:2] = [1.0/2.0, 1.0/2.0] + coef[3, 1:2] = [-1.0/2.0, 3.0/2.0] + elseif spatial_order == 3 + coef[1, 1:3] = [11.0/6.0, -7.0/6.0, 1.0/3.0] + coef[2, 1:3] = [1.0/3.0, 5.0/6.0, -1.0/6.0] + coef[3, 1:3] = [-1.0/6.0, 5.0/6.0, 1.0/3.0] + coef[4, 1:3] = [1.0/3.0, -7.0/6.0, 11.0/6.0] + elseif spatial_order == 4 + coef[1, 1:4] = [25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0] + coef[2, 1:4] = [1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0] + coef[3, 1:4] = [-1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0] + coef[4, 1:4] = [1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0] + coef[5, 1:4] = [-1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0] + elseif spatial_order == 5 + coef[1, 1:5] = [137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0] + coef[2, 1:5] = [1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0] + coef[3, 1:5] = [-1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0] + coef[4, 1:5] = [1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0] + coef[5, 1:5] = [-1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0] + coef[6, 1:5] = [1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0] + elseif spatial_order == 6 + coef[1, 1:6] = [49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0] + coef[2, 1:6] = [1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0] + coef[3, 1:6] = [-1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0] + coef[4, 1:6] = [1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0] + coef[5, 1:6] = [-1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0] + coef[6, 1:6] = [1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0] + coef[7, 1:6] = [-1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0] + elseif spatial_order == 7 + coef[1, 1:7] = [363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0] + coef[2, 1:7] = [1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0] + coef[3, 1:7] = [-1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0] + coef[4, 1:7] = [1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0] + coef[5, 1:7] = [-1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0] + coef[6, 1:7] = [1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0] + coef[7, 1:7] = [-1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0] + coef[8, 1:7] = [1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0] + else + error("ENO 系数未实现 order=$spatial_order") + end +end + +# ---------------------- ENO 重构器 ---------------------- +mutable struct EnoReconstructor + spatial_order::Int + ntcells::Int + lmc::Vector{Int} + coef::Matrix{Float64} + dd::Matrix{Float64} + + function EnoReconstructor(spatial_order::Int, ntcells::Int) + lmc = zeros(Int, ntcells) + coef = zeros(Float64, spatial_order + 1, spatial_order) + dd = zeros(Float64, spatial_order, ntcells) + _init_eno_coef!(spatial_order, coef) + new(spatial_order, ntcells, lmc, coef, dd) + end +end + +function reconstruct(rec::EnoReconstructor, q::Vector{Float64}, cfd::Any) + # 1. 差商计算 (dd[1,:] = q) + @views rec.dd[1, :] .= q + for m in 2:rec.spatial_order + for j in 1:(rec.ntcells - m + 1) + rec.dd[m, j] = rec.dd[m-1, j+1] - rec.dd[m-1, j] + end + end + + # 2. 选择 smoothest stencil + domain = cfd.domain + for i in (domain.ist - 1):(domain.ied) # Python: range(ist-1, ied+1) → ied+1-1 = ied + rec.lmc[i] = i + for m in 2:rec.spatial_order + if abs(rec.dd[m, rec.lmc[i] - 1]) < abs(rec.dd[m, rec.lmc[i]]) + rec.lmc[i] -= 1 + end + end + end + + # 3. 重构界面值 + solution = cfd.solution + for i in domain.ist:(domain.ied) # Python: range(ist, ied+1) → ied+1-1 = ied + j = i - domain.ist + 1 # Julia 1-based + k1 = rec.lmc[i - 1] + k2 = rec.lmc[i] + r1 = (i - 1) - k1 + 1 + r2 = i - k2 + 1 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in 1:rec.spatial_order + solution.q_face_left[j] += q[k1 + m - 1] * rec.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m - 1] * rec.coef[r2, m] + end + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03a/src/reconstructor/factory.jl b/example/1d-linear-convection/weno3/julia/03a/src/reconstructor/factory.jl new file mode 100644 index 00000000..70b0cf9f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03a/src/reconstructor/factory.jl @@ -0,0 +1,38 @@ +# src/reconstructor/factory.jl + +""" +ReconstructorFactory +对标 Python: ReconstructorFactory.create(config, domain) +""" +module ReconstructorFactory + +include("../utils.jl") + +# ✅ 不要用 using ..EnoReconstructor(它们不是模块) +# 直接通过 Main. 引用顶层定义的类型 + +function create(config::Any, domain::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + # 处理 WENO 默认命名(如 Python) + if scheme == "weno" + order = getfield_safe(config, :spatial_order, 5) + scheme = "weno$(order)" + end + + if scheme == "eno" + order = getfield_safe(config, :spatial_order, 3) + return Main.EnoReconstructor(order, domain.ntcells) + elseif scheme == "weno3" + return Main.Weno3Reconstructor() + else + error("不支持的重建格式: $scheme(仅支持 eno/weno3)") + end +end + +end # module ReconstructorFactory + +export ReconstructorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03a/src/reconstructor/weno3.jl b/example/1d-linear-convection/weno3/julia/03a/src/reconstructor/weno3.jl new file mode 100644 index 00000000..2b6fe1ab --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03a/src/reconstructor/weno3.jl @@ -0,0 +1,65 @@ +# julia/reconstructor/weno3.jl +""" +WENO3 重构器(与 reconstructor/weno3.py 完全同构) +""" + +mutable struct Weno3Reconstructor + # 无字段,与 Python 一致 +end + +function reconstruct(rec::Weno3Reconstructor, q::Vector{Float64}, cfd::Any) + domain = cfd.domain + solution = cfd.solution + _reconstruct_left_interfaces(domain, q, solution.q_face_left) + _reconstruct_right_interfaces(domain, q, solution.q_face_right) +end + +function _reconstruct_left_interfaces(domain, u, qL) + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in (domain.ist - 1):(domain.ied - 1) + j = i - (domain.ist - 1) + 1 # ← Julia 1-based: j = i - (ist-1) 对应 Python j = i - (ist-1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = _reconstruct_from_left_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_right_interfaces(domain, u, qR) + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in domain.ist:domain.ied + j = i - domain.ist + 1 # ← Julia 1-based: j = i - ist 对应 Python j = i - ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = _reconstruct_from_right_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_from_left_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -0.5*v1 + 1.5*v2 # r=1 stencil + q1 = 0.5*v2 + 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end + +function _reconstruct_from_right_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 0.5*v1 + 0.5*v2 # r=1 stencil + q1 = 1.5*v2 - 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03a/src/registry.jl b/example/1d-linear-convection/weno3/julia/03a/src/registry.jl new file mode 100644 index 00000000..6acd9aaa --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03a/src/registry.jl @@ -0,0 +1,164 @@ +""" +统一注册系统 + 通用工厂 +- ComponentRegistry: 组件注册表 +- @register_component: 装饰器宏 +- BaseFactory: 通用工厂接口 +""" + +module ComponentRegistry + +# 注册表:Dict{category => Dict{name => constructor}} +const _REGISTRIES = Dict{String, Dict{String, Function}}() +const _VERBOSE = Ref(true) + +# ---------------------- 注册核心逻辑 ---------------------- +""" + register(category::String, name::String, ctor::Function) + +注册一个组件构造函数到指定类别。 +如果已存在同名组件且不同,则发出警告。 +""" +function register(category::String, name::String, ctor::Function) + if !haskey(_REGISTRIES, category) + _REGISTRIES[category] = Dict{String, Function}() + end + + registry = _REGISTRIES[category] + if haskey(registry, name) + if registry[name] !== ctor && _VERBOSE[] + @warn "覆盖注册: $category.$name" + end + end + + registry[name] = ctor + if _VERBOSE[] + println("✅ 已注册: $category.$name -> $(nameof(ctor))") + end +end + +""" + create(category::String, name::String, args...; kwargs...) + +从注册表创建组件实例。 +""" +function create(category::String, name::String, args...; kwargs...) + if !haskey(_REGISTRIES, category) + error("❌ 未知类别: $category (可用: $(collect(keys(_REGISTRIES))))") + end + + registry = _REGISTRIES[category] + lname = lowercase(name) + if !haskey(registry, lname) + available = sort(collect(keys(registry))) + error("❌ 未找到: $category.$name (可用: $available)") + end + + return registry[lname](args...; kwargs...) +end + +""" + list_all() + +返回所有已注册组件(按类别)。 +""" +function list_all() + return Dict(cat => sort(collect(keys(reg))) for (cat, reg) in _REGISTRIES) +end + +""" + set_verbose(flag::Bool) + +开启/关闭注册提示。 +""" +function set_verbose(flag::Bool) + _VERBOSE[] = flag +end + +end # module ComponentRegistry + + +# ---------------------- 装饰器宏:@register_component ---------------------- +""" +@register_component(category, [name]) + +用法: + @register_component("boundary", "periodic") + struct PeriodicBoundary ... + +若省略 name,则使用类型名的小写形式。 +""" +macro register_component(category::String, name_expr) + error("@register_component 需在类型定义前使用,且必须在模块顶层") +end + +macro register_component(category::String) + error("@register_component(category, name) 需指定 name 或在类型后使用") +end + +# 重载:@register_component("category", "name") struct X ... end +macro register_component(category::String, name::String, ex) + if !Meta.isexpr(ex, :struct) + error("@register_component 必须作用于 struct 定义") + end + + struct_name = ex.args[2] + if Meta.isexpr(struct_name, :curly) + struct_name = struct_name.args[1] + end + + # 插入注册调用(在模块顶层) + quote + $(esc(ex)) + $(ComponentRegistry).register($(category), $(name), $(esc(struct_name))) + end +end + +# 重载:@register_component("category") struct X ... end → name = lowercase(nameof(X)) +macro register_component(category::String, ex) + if !Meta.isexpr(ex, :struct) + error("@register_component 必须作用于 struct 定义") + end + + struct_name = ex.args[2] + if Meta.isexpr(struct_name, :curly) + struct_name = struct_name.args[1] + end + + name_str = string(struct_name) |> lowercase + + quote + $(esc(ex)) + $(ComponentRegistry).register($(category), $(name_str), $(esc(struct_name))) + end +end + + +# ---------------------- 通用工厂 ---------------------- +module BaseFactory + +using ..ComponentRegistry + +""" + create_component(category::String, name::String, args...; kwargs...) + +通用工厂接口,与 Python BaseFactory.create_component 行为一致。 +""" +function create_component(category::String, name::String, args...; kwargs...) + return ComponentRegistry.create(category, name, args...; kwargs...) +end + +""" + get_available_components(category::String) + +列出某类别下所有可用组件。 +""" +function get_available_components(category::String) + all = ComponentRegistry.list_all() + return get(all, category, String[]) +end + +end # module BaseFactory + + +# 导出接口 +export ComponentRegistry, BaseFactory, @register_component \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03a/src/residual.jl b/example/1d-linear-convection/weno3/julia/03a/src/residual.jl new file mode 100644 index 00000000..11a88593 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03a/src/residual.jl @@ -0,0 +1,69 @@ +# julia/residual.jl +""" +残差计算器(与 residual.py 完全同构) +- 封装重建→通量→散度完整流程 +- 依赖 cfd 的多个字段 +""" + +include("mesh.jl") + +mutable struct ResidualCalculator + cfd::Any + config::Any + domain::Any + solution::Any + mesh::Mesh + reconstructor::Any + flux_calculator::Any + + function ResidualCalculator(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + mesh = domain.mesh + reconstructor = cfd.reconstructor + + # ✅ 内部创建 flux_calculator(对标 Python) + flux_calculator = FluxCalculatorFactory.create(cfd) + + new(cfd, config, domain, solution, mesh, reconstructor, flux_calculator) + end +end + + +""" +计算完整残差(对外唯一接口) +""" +function compute!(calc::ResidualCalculator) + _reconstruct(calc) + _compute_inviscid_flux(calc) + _compute_flux_divergence(calc) +end + +""" +私有方法:界面值重建 +""" +function _reconstruct(calc::ResidualCalculator) + reconstruct(calc.reconstructor, calc.solution.u, calc.cfd) +end + +""" +私有方法:计算无粘通量 +""" +function _compute_inviscid_flux(calc::ResidualCalculator) + compute!(calc.flux_calculator, + calc.solution.q_face_left, + calc.solution.q_face_right, + calc.solution.flux) +end + +""" +私有方法:计算通量散度(残差 = -dF/dx) +""" +function _compute_flux_divergence(calc::ResidualCalculator) + solution = calc.solution + mesh = calc.mesh + for i in 1:mesh.ncells + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / mesh.dx + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03a/src/solution.jl b/example/1d-linear-convection/weno3/julia/03a/src/solution.jl new file mode 100644 index 00000000..7044c001 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03a/src/solution.jl @@ -0,0 +1,60 @@ +# julia/solution.jl +""" +Solution 结构体(与真实 solution.py 完全同构) +字段顺序、方法、逻辑 1:1 对齐 +""" + +include("mesh.jl") +include("domain.jl") +include("initial_condition.jl") + +mutable struct Solution + domain::Domain + q_face_left::Vector{Float64} + q_face_right::Vector{Float64} + flux::Vector{Float64} + res::Vector{Float64} + u::Vector{Float64} + un::Vector{Float64} + + function Solution(config::Any, domain::Domain) + mesh = domain.mesh + + q_face_left = zeros(Float64, mesh.nnodes) + q_face_right = zeros(Float64, mesh.nnodes) + flux = zeros(Float64, mesh.nnodes) + res = zeros(Float64, mesh.ncells) + u = zeros(Float64, domain.ntcells) + un = zeros(Float64, domain.ntcells) + + sol = new(domain, q_face_left, q_face_right, flux, res, u, un) + initialize_from_config(sol, config) + return sol + end +end + +""" +重置解数组为初始状态 +""" +function reset_solution(sol::Solution) + fill!(sol.u, 0.0) + fill!(sol.un, 0.0) +end + +""" +根据配置初始化场 +""" + +function initialize_from_config(sol::Solution, config::Any) + ic_type = getfield_safe(config, :ic_type, "step") + ic = InitialConditionFactory.create(ic_type, config) + apply(ic, sol) +end + +""" +更新旧场:un = u +""" +function update_old_field(sol::Solution) + sol.un .= sol.u # 等价于 Python 的 un[:] = u[:] +end + diff --git a/example/1d-linear-convection/weno3/julia/03a/src/solver.jl b/example/1d-linear-convection/weno3/julia/03a/src/solver.jl new file mode 100644 index 00000000..d8193f0e --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03a/src/solver.jl @@ -0,0 +1,130 @@ +# julia/solver.jl +""" +CFD 求解器主类(与 solver.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("initial_condition.jl") +include("boundary.jl") +include("flux.jl") +include("residual.jl") +include("time_integration.jl") +include("reconstructor/eno.jl") +include("reconstructor/weno3.jl") +include("reconstructor/factory.jl") + +# 导入工厂模块(必须在顶层!) +using .FluxCalculatorFactory +using .TimeIntegratorFactory +using .BoundaryConditionFactory +using .ReconstructorFactory + +# ---------------------- Cfd 求解器 ---------------------- +mutable struct Cfd + config::Any + domain::Domain + solution::Solution + reconstructor::Any + residual_calculator::ResidualCalculator + integrator::Any + boundary_condition::Any + result::Dict{String, Any} + + function Cfd(config::Any, mesh::Mesh) + domain = Domain(config, mesh) + solution = Solution(config, domain) + reconstructor = ReconstructorFactory.create(config, domain) + + # 1. 初始上下文(仅包含不依赖其他组件的字段) + full_cfd = ( + config = config, + domain = domain, + solution = solution, + reconstructor = reconstructor + # 注意:不预先占位 nothing! + ) + + # 2. 创建 boundary_condition(只依赖 config + domain) + boundary_condition = BoundaryConditionFactory.create(full_cfd) + full_cfd = merge(full_cfd, (boundary_condition = boundary_condition,)) + + # 3. 创建 residual_calculator(依赖上面所有字段) + residual_calculator = ResidualCalculator(full_cfd) + full_cfd = merge(full_cfd, (residual_calculator = residual_calculator,)) + + # 4. 创建 integrator(依赖 residual_calculator 等) + integrator = TimeIntegratorFactory.create(full_cfd) + full_cfd = merge(full_cfd, (integrator = integrator,)) + + # 5. 注入完整 self 到组件(确保它们能访问彼此) + residual_calculator.cfd = full_cfd + integrator.base.cfd = full_cfd + + result = Dict{String, Any}() + new(config, domain, solution, reconstructor, residual_calculator, integrator, boundary_condition, result) + end +end + +""" +通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界 +""" +function exact_solution(cfd::Cfd) + x = cfd.domain.mesh.xcc + T = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = @. (x - c * T + L) % L + + # ✅ 使用工厂创建初始条件(对标 Python) + ic = InitialConditionFactory.create(cfd.config.ic_type, cfd.config) + + return evaluate_at(ic, x_shifted) +end + +""" +主求解循环 +""" +function run!(cfd::Cfd) + # 应用初始边界条件并同步 old field + apply!(cfd.boundary_condition, cfd.solution.u) + update_old_field(cfd.solution) + + t = 0.0 + dt_old = cfd.config.dt + dt = dt_old + + while t < cfd.config.final_time + if t + dt > cfd.config.final_time + dt = cfd.config.final_time - t + end + #@show t, dt, maximum(cfd.solution.u), minimum(cfd.solution.u) + # 执行时间步 + step(cfd.integrator, dt) + t += dt + end + + # 恢复 dt + cfd.config.dt = dt_old + + # 整理结果 + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied-1] # Python: [ist:ied] + analytical = exact_solution(cfd) + + cfd.result = Dict( + "x" => cfd.domain.mesh.xcc, + "numerical" => u_numerical, + "analytical" => analytical, + "config" => Dict( + "scheme" => cfd.config.recon_scheme, + "order" => cfd.config.spatial_order, + "rk_order" => cfd.config.rk_order, + "final_time" => cfd.config.final_time + ) + ) + + return u_numerical +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03a/src/time_integration.jl b/example/1d-linear-convection/weno3/julia/03a/src/time_integration.jl new file mode 100644 index 00000000..add564ba --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03a/src/time_integration.jl @@ -0,0 +1,169 @@ +# julia/time_integration.jl +""" +时间推进器模块(与 time_integration.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("residual.jl") +include("boundary.jl") + +# ---------------------- 抽象时间推进器基类 ---------------------- +abstract type TimeIntegrator end + +mutable struct TimeIntegratorBase <: TimeIntegrator + cfd::Any + config::Any + domain::Domain + solution::Solution + residual_calculator::Any # ResidualCalculator +end + +function TimeIntegratorBase(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + residual_calculator = cfd.residual_calculator + TimeIntegratorBase(cfd, config, domain, solution, residual_calculator) +end + +function compute_residual(integrator::TimeIntegratorBase) + compute!(integrator.residual_calculator) +end + +function apply_boundary(integrator::TimeIntegratorBase) + apply!(integrator.cfd.boundary_condition, integrator.solution.u) +end + +function map_idx(integrator::TimeIntegratorBase, i::Int) + return i - integrator.domain.ist + 1 # ← +1 转为 1-based +end + +# ---------------------- RK1Integrator ---------------------- +mutable struct RK1Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK1Integrator(cfd::Any) + RK1Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK1Integrator, dt::Float64) + compute_residual(integrator.base) + for i in integrator.base.domain.ist:(integrator.base.domain.ied - 1) + j = map_idx(integrator.base, i) + integrator.base.solution.u[i] += dt * integrator.base.solution.res[j] + end + apply_boundary(integrator.base) + update_old_field(integrator.base.solution) +end + +# ---------------------- RK2Integrator ---------------------- +mutable struct RK2Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK2Integrator(cfd::Any) + RK2Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK2Integrator, dt::Float64) + base = integrator.base + # 阶段1:预测步 + compute_residual(base) + u_pred = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u_pred[i] += dt * base.solution.res[j] + end + base.solution.u .= u_pred + apply_boundary(base) + # 阶段2:校正步 + compute_residual(base) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = 0.5 * base.solution.un[i] + 0.5 * base.solution.u[i] + 0.5 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- RK3Integrator ---------------------- +mutable struct RK3Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK3Integrator(cfd::Any) + RK3Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK3Integrator, dt::Float64) + base = integrator.base + # 阶段1 + compute_residual(base) + u1 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u1[i] += dt * base.solution.res[j] + end + base.solution.u .= u1 + apply_boundary(base) + # 阶段2 + compute_residual(base) + u2 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u2[i] = 0.75 * base.solution.un[i] + 0.25 * base.solution.u[i] + 0.25 * dt * base.solution.res[j] + end + base.solution.u .= u2 + apply_boundary(base) + # 阶段3 + compute_residual(base) + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = c1 * base.solution.un[i] + c2 * base.solution.u[i] + c3 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- TimeIntegratorFactory ---------------------- +module TimeIntegratorFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "时间积分器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + rk_order = cfd.config.rk_order + if !(rk_order isa Integer) || rk_order < 1 + error("rk_order 必须为正整数,当前值: $rk_order") + end + + name = "rk$rk_order" + if !haskey(_REGISTRY, name) + available = sort(collect(keys(_REGISTRY))) + error("未注册的时间积分器: '$name'。可用选项: $available") + end + + return _REGISTRY[name](cfd) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("rk1", cfd -> Main.RK1Integrator(cfd)) +register("rk2", cfd -> Main.RK2Integrator(cfd)) +register("rk3", cfd -> Main.RK3Integrator(cfd)) + +end # module TimeIntegratorFactory + +export TimeIntegratorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03a/src/utils.jl b/example/1d-linear-convection/weno3/julia/03a/src/utils.jl new file mode 100644 index 00000000..3786cd1a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03a/src/utils.jl @@ -0,0 +1,9 @@ +# src/utils.jl + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end diff --git a/example/1d-linear-convection/weno3/julia/03b/examples/run_eno_weno.jl b/example/1d-linear-convection/weno3/julia/03b/examples/run_eno_weno.jl new file mode 100644 index 00000000..da24e6cd --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03b/examples/run_eno_weno.jl @@ -0,0 +1,50 @@ +# examples/run_eno_weno.jl +""" +1:1 复刻 run_eno_weno.py 的 Julia 版本 +""" + +include("../src/config.jl") +include("../src/mesh.jl") +include("../src/solver.jl") +include("../src/plotter.jl") + +function performEnoWenoAnalysis() + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + println("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + with_reconstruction(config_eno3, "eno", 3) # 显式指定 3 阶 + config_eno3.dt = 0.0025 # 覆盖默认值 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + run!(cfd_eno3) # 求解并生成 result 字典 + + # 3. 配置并运行 WENO3 求解 + println("Running WENO3 solver...") + config_weno3 = CfdConfig() + with_reconstruction(config_weno3, "weno", 3) # 显式指定 3 阶(WENO 默认 5 阶) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + run!(cfd_weno3) + + # 5. 绘制 ENO/WENO 对比图 + println("Plotting comparison results...") + plot_eno_weno_comparison( + cfd_eno3.result, + cfd_weno3.result; + save_path="eno_weno_comparison.png" + ) + + return cfd_eno3, cfd_weno3 +end + +# 主程序入口 +if abspath(PROGRAM_FILE) == @__FILE__ + performEnoWenoAnalysis() + println("Analysis completed!") +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03b/src/boundary.jl b/example/1d-linear-convection/weno3/julia/03b/src/boundary.jl new file mode 100644 index 00000000..82f58b71 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03b/src/boundary.jl @@ -0,0 +1,121 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +#using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end + + +# ---------------------- BoundaryConditionFactory ---------------------- +module BoundaryConditionFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "边界条件 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + bc_type = cfd.config.boundary_type + if !(bc_type isa AbstractString) + error("boundary_type 必须为字符串,当前值: $bc_type") + end + + if !haskey(_REGISTRY, bc_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的边界条件: '$bc_type'。可用选项: $available") + end + + return _REGISTRY[bc_type](cfd) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("periodic", cfd -> Main.PeriodicBoundary(cfd)) +register("dirichlet", cfd -> Main.DirichletBoundary(cfd)) +register("neumann", cfd -> Main.NeumannBoundary(cfd)) + +end # module BoundaryConditionFactory + +export BoundaryConditionFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03b/src/config.jl b/example/1d-linear-convection/weno3/julia/03b/src/config.jl new file mode 100644 index 00000000..dd34fe7a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03b/src/config.jl @@ -0,0 +1,69 @@ +# julia/config.jl +""" +CfdConfig:与 Python config.py 完全同构 +""" +mutable struct CfdConfig + ic_type::String + recon_scheme::String + flux_type::String + rk_order::Int + wave_speed::Float64 + final_time::Float64 + dt::Float64 + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + spatial_order::Int + + function CfdConfig() + new( + "step", + "eno", + "rusanov", + 1, + 1.0, + 0.625, + 0.025, + "periodic", + 1.0, + 2.0, + 2 + ) + end +end + +""" +专用配置:重建方案(链式调用) +""" +function with_reconstruction(cfg::CfdConfig, scheme::String, order::Union{Int, Nothing}=nothing) + cfg.recon_scheme = lowercase(scheme) + + if order !== nothing + cfg.spatial_order = order + else + if startswith(cfg.recon_scheme, "weno") + cfg.spatial_order = 5 + elseif cfg.recon_scheme == "eno" + cfg.spatial_order = 3 + else + error("不支持的重建格式:$scheme(仅支持 eno/weno)") + end + end + + return cfg # 支持链式调用 +end + +""" +专用配置:边界条件(链式调用) +""" +function with_boundary(cfg::CfdConfig, bc_type::String; left_value=nothing, right_value=nothing) + cfg.boundary_type = bc_type + if left_value !== nothing + cfg.left_boundary_value = left_value + end + if right_value !== nothing + cfg.right_boundary_value = right_value + end + return cfg +end + diff --git a/example/1d-linear-convection/weno3/julia/03b/src/domain.jl b/example/1d-linear-convection/weno3/julia/03b/src/domain.jl new file mode 100644 index 00000000..eec26114 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03b/src/domain.jl @@ -0,0 +1,58 @@ +# julia/domain.jl +""" +Domain 结构体(与 domain.py 完全同构) +- 保存 config +- nghosts 计算逻辑 1:1 +- ist/ied 为 0-based(与 Python 一致) +""" + +include("mesh.jl") +include("utils.jl") + +mutable struct Domain + config::Any # 保存 config(与 Python 一致) + mesh::Mesh + nghosts::Int + ist::Int # 0-based + ied::Int # 0-based + ntcells::Int +end + +function Domain(config::Any, mesh::Mesh) + nghosts = _calc_nghosts(config) + ist = nghosts + 1 # ← 1-based 起始索引 + ied = ist + mesh.ncells # ← 1-based 结束索引(不包含) + ntcells = mesh.ncells + 2 * nghosts + Domain(config, mesh, nghosts, ist, ied, ntcells) +end + +function _calc_nghosts(config::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + order = getfield_safe(config, :spatial_order, 2) + + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + if scheme == "eno" + nghosts = order + elseif startswith(scheme, "weno") + nghosts = order ÷ 2 + 1 + else + error("未知重建格式 $(scheme),无法计算ghost层!") + end + + if nghosts <= 0 + error("计算得到的ghost层数量无效:$(nghosts)(阶数$(order),格式$(scheme))") + end + + return nghosts +end + +function is_physical_cell(domain::Domain, idx::Int) + return domain.ist <= idx < domain.ied +end + +function get_physical_indices(domain::Domain) + return domain.ist:(domain.ied - 1) +end diff --git a/example/1d-linear-convection/weno3/julia/03b/src/flux/base.jl b/example/1d-linear-convection/weno3/julia/03b/src/flux/base.jl new file mode 100644 index 00000000..58dd6cc9 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03b/src/flux/base.jl @@ -0,0 +1,7 @@ +# src/flux/base.jl +""" +抽象通量计算器接口 +Julia 无 ABC,用文档约定: +- 所有子类型必须实现 `compute!(calc, qL, qR, flux)` +""" +abstract type InviscidFluxCalculator end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03b/src/flux/engquist_osher.jl b/example/1d-linear-convection/weno3/julia/03b/src/flux/engquist_osher.jl new file mode 100644 index 00000000..c0de183d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03b/src/flux/engquist_osher.jl @@ -0,0 +1,25 @@ +# src/flux/engquist_osher.jl +mutable struct EngquistOsherFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function EngquistOsherFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::EngquistOsherFluxCalculator, qL::Vector{Float64}, qR::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + c = calc.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = qL[i] + u_R = qR[i] + flux[i] = cp * u_L + cm * u_R + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03b/src/flux/factory.jl b/example/1d-linear-convection/weno3/julia/03b/src/flux/factory.jl new file mode 100644 index 00000000..6ae4917d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03b/src/flux/factory.jl @@ -0,0 +1,26 @@ +# src/flux/factory.jl + +module FluxCalculatorFactory + +# 全局注册表 +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "通量计算器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + flux_type = cfd.config.flux_type + if !haskey(_REGISTRY, flux_type) + error("未注册的通量计算器: '$flux_type'。可用: $(keys(_REGISTRY))") + end + return _REGISTRY[flux_type](cfd) +end + +register("rusanov", cfd -> Main.RusanovFluxCalculator(cfd)) +register("engquist-osher", cfd -> Main.EngquistOsherFluxCalculator(cfd)) + +end # module FluxCalculatorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03b/src/flux/flux.jl b/example/1d-linear-convection/weno3/julia/03b/src/flux/flux.jl new file mode 100644 index 00000000..533f034c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03b/src/flux/flux.jl @@ -0,0 +1,10 @@ +# src/flux/flux.jl + +# 加载组件(顺序很重要!) +include("base.jl") +include("rusanov.jl") +include("engquist_osher.jl") +include("factory.jl") + +# 导出(如果你未来用模块) +# export InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03b/src/flux/rusanov.jl b/example/1d-linear-convection/weno3/julia/03b/src/flux/rusanov.jl new file mode 100644 index 00000000..70c20fc3 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03b/src/flux/rusanov.jl @@ -0,0 +1,28 @@ +# src/flux/rusanov.jl + +mutable struct RusanovFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function RusanovFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::RusanovFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = calc.wave_speed + c_R = calc.wave_speed + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03b/src/initial_condition.jl b/example/1d-linear-convection/weno3/julia/03b/src/initial_condition.jl new file mode 100644 index 00000000..1b9993aa --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03b/src/initial_condition.jl @@ -0,0 +1,112 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end + +# ---------------------- InitialConditionFactory ---------------------- +module InitialConditionFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "初始条件 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(ic_type::String, config::Any) + if !(ic_type isa AbstractString) + error("ic_type 必须为字符串,当前值: $ic_type") + end + + if !haskey(_REGISTRY, ic_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的初始条件: '$ic_type'。可用选项: $available") + end + + return _REGISTRY[ic_type](config) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("step", config -> Main.StepFunctionIC(config)) +register("sin", config -> Main.SineWaveIC(config)) +register("gaussian", config -> Main.GaussianPulseIC(config)) + +end # module InitialConditionFactory + +export InitialConditionFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03b/src/mesh.jl b/example/1d-linear-convection/weno3/julia/03b/src/mesh.jl new file mode 100644 index 00000000..1b0dd4bf --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03b/src/mesh.jl @@ -0,0 +1,45 @@ +# julia/mesh.jl +""" +Mesh 结构体(与提供的 mesh.py 完全同构) +- 字段名、初始化顺序、循环逻辑完全一致 +- 不做任何泛化或优化 +""" + +mutable struct Mesh + xmin::Float64 + xmax::Float64 + ncells::Int + nnodes::Int + nx::Int + x::Vector{Float64} + xcc::Vector{Float64} + L::Float64 # 在 init_mesh 中赋值 + dx::Float64 # 在 init_mesh 中赋值 + + function Mesh() + # 与 Python __init__ 完全一致 + xmin = 0.0 + xmax = 2.0 + ncells = 40 + nnodes = ncells + 1 + nx = ncells + x = zeros(Float64, nnodes) + xcc = zeros(Float64, ncells) + + # 调用 init_mesh(模拟 Python) + L = xmax - xmin + dx = L / ncells + + # Generate node coordinates: for i in range(self.nnodes) + for i in 1:nnodes + x[i] = xmin + (i - 1) * dx # Python i → Julia i-1 + end + + # Generate cell center coordinates + for i in 1:ncells + xcc[i] = 0.5 * (x[i] + x[i+1]) + end + + new(xmin, xmax, ncells, nnodes, nx, x, xcc, L, dx) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03b/src/plotter.jl b/example/1d-linear-convection/weno3/julia/03b/src/plotter.jl new file mode 100644 index 00000000..a77d8d68 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03b/src/plotter.jl @@ -0,0 +1,147 @@ +# julia/plotter.jl +""" +CFDPlotter 的 Julia 实现(通过 PythonCall.jl 调用 Matplotlib) +确保与 Python plotter.py 行为完全一致 +""" + +using PythonCall + +# 初始化 Python 环境(加载 matplotlib, inflect) +const plt = pyimport("matplotlib.pyplot") +const inflect = pyimport("inflect") + +mutable struct CFDPlotter + default_styles::Dict{String, Any} + p::Py +end + +function CFDPlotter() + default_styles = Dict{String, Any}( + "numerical" => Dict( + :color => "blue", + :linestyle => "-", + :marker => "o", + :markerfacecolor => "none" + ), + "analytical" => Dict( + :color => "red", + :linestyle => "--", + :marker => "", + :linewidth => 1.5 + ), + "comparison" => [ + Dict(:color => "black", :linestyle => "-", :marker => "o", :markerfacecolor => "none"), + Dict(:color => "blue", :linestyle => "--", :marker => "s", :markerfacecolor => "none"), + Dict(:color => "green", :linestyle => ":", :marker => "^", :markerfacecolor => "none") + ] + ) + p = inflect.engine() + CFDPlotter(default_styles, p) +end + +""" +轻量即时绘图(快速验证结果) +""" +function plot_quick(plotter::CFDPlotter, cfd_result::Dict; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + rk_order = cfd_result["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = cfd_result["config"]["final_time"] + order = cfd_result["config"]["order"] + scheme = uppercase(cfd_result["config"]["scheme"]) + actual_title = "1D Convection (t=$(final_time))\n$(order)th-order $(scheme) + $(rk_str)-order RK" + end + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"]; + label="Numerical ($(uppercase(cfd_result["config"]["scheme"])))", + plotter.default_styles["numerical"]..., + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + # 通用样式 + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +""" +多格式/多精度对比绘图 +""" +function plot_comparison(plotter::CFDPlotter, result_list::Vector{Dict{String, Any}}; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + schemes = [uppercase(r["config"]["scheme"]) * string(r["config"]["order"]) for r in result_list] + rk_order = result_list[1]["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = result_list[1]["config"]["final_time"] + actual_title = "1D Convection Comparison (t=$(final_time))\n$(join(schemes, ", ")) + $(rk_str)-order RK" + end + + # 绘制多个数值解 + for (i, res) in enumerate(result_list) + style = plotter.default_styles["comparison"][mod1(i, length(plotter.default_styles["comparison"]))] + label = "Numerical ($(uppercase(res["config"]["scheme"]))$(res["config"]["order"]))" + plt.plot( + res["x"], res["numerical"]; + label=label, + style..., + markersize=5, linewidth=0.5 + ) + end + + # 绘制解析解 + plt.plot( + result_list[1]["x"], result_list[1]["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +function _set_common_style(plotter::CFDPlotter, title::String) + plt.title(title, fontsize=12) + plt.xlabel("x", fontsize=10) + plt.ylabel("u", fontsize=10) + plt.legend(fontsize=9) + plt.grid(true, color="gray", linestyle="--", linewidth=0.5, alpha=0.7) + plt.tight_layout() +end + +""" +快捷函数:ENO/WENO对比绘图 +""" +function plot_eno_weno_comparison(eno_result::Dict, weno_result::Dict; save_path=nothing) + plotter = CFDPlotter() + plot_comparison(plotter, [eno_result, weno_result]; save_path=save_path) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03b/src/reconstructor/eno.jl b/example/1d-linear-convection/weno3/julia/03b/src/reconstructor/eno.jl new file mode 100644 index 00000000..e78a636f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03b/src/reconstructor/eno.jl @@ -0,0 +1,107 @@ +# julia/reconstructor/eno.jl +""" +ENO 重构器(与 reconstructor/eno.py 完全同构) +""" + +# ---------------------- ENO 系数初始化 ---------------------- +function _init_eno_coef!(spatial_order::Int, coef::Matrix{Float64}) + if spatial_order == 1 + coef[1, 1] = 1.0 + coef[2, 1] = 1.0 + elseif spatial_order == 2 + coef[1, 1:2] = [3.0/2.0, -1.0/2.0] + coef[2, 1:2] = [1.0/2.0, 1.0/2.0] + coef[3, 1:2] = [-1.0/2.0, 3.0/2.0] + elseif spatial_order == 3 + coef[1, 1:3] = [11.0/6.0, -7.0/6.0, 1.0/3.0] + coef[2, 1:3] = [1.0/3.0, 5.0/6.0, -1.0/6.0] + coef[3, 1:3] = [-1.0/6.0, 5.0/6.0, 1.0/3.0] + coef[4, 1:3] = [1.0/3.0, -7.0/6.0, 11.0/6.0] + elseif spatial_order == 4 + coef[1, 1:4] = [25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0] + coef[2, 1:4] = [1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0] + coef[3, 1:4] = [-1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0] + coef[4, 1:4] = [1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0] + coef[5, 1:4] = [-1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0] + elseif spatial_order == 5 + coef[1, 1:5] = [137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0] + coef[2, 1:5] = [1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0] + coef[3, 1:5] = [-1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0] + coef[4, 1:5] = [1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0] + coef[5, 1:5] = [-1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0] + coef[6, 1:5] = [1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0] + elseif spatial_order == 6 + coef[1, 1:6] = [49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0] + coef[2, 1:6] = [1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0] + coef[3, 1:6] = [-1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0] + coef[4, 1:6] = [1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0] + coef[5, 1:6] = [-1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0] + coef[6, 1:6] = [1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0] + coef[7, 1:6] = [-1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0] + elseif spatial_order == 7 + coef[1, 1:7] = [363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0] + coef[2, 1:7] = [1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0] + coef[3, 1:7] = [-1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0] + coef[4, 1:7] = [1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0] + coef[5, 1:7] = [-1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0] + coef[6, 1:7] = [1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0] + coef[7, 1:7] = [-1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0] + coef[8, 1:7] = [1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0] + else + error("ENO 系数未实现 order=$spatial_order") + end +end + +# ---------------------- ENO 重构器 ---------------------- +mutable struct EnoReconstructor + spatial_order::Int + ntcells::Int + lmc::Vector{Int} + coef::Matrix{Float64} + dd::Matrix{Float64} + + function EnoReconstructor(spatial_order::Int, ntcells::Int) + lmc = zeros(Int, ntcells) + coef = zeros(Float64, spatial_order + 1, spatial_order) + dd = zeros(Float64, spatial_order, ntcells) + _init_eno_coef!(spatial_order, coef) + new(spatial_order, ntcells, lmc, coef, dd) + end +end + +function reconstruct(rec::EnoReconstructor, q::Vector{Float64}, cfd::Any) + # 1. 差商计算 (dd[1,:] = q) + @views rec.dd[1, :] .= q + for m in 2:rec.spatial_order + for j in 1:(rec.ntcells - m + 1) + rec.dd[m, j] = rec.dd[m-1, j+1] - rec.dd[m-1, j] + end + end + + # 2. 选择 smoothest stencil + domain = cfd.domain + for i in (domain.ist - 1):(domain.ied) # Python: range(ist-1, ied+1) → ied+1-1 = ied + rec.lmc[i] = i + for m in 2:rec.spatial_order + if abs(rec.dd[m, rec.lmc[i] - 1]) < abs(rec.dd[m, rec.lmc[i]]) + rec.lmc[i] -= 1 + end + end + end + + # 3. 重构界面值 + solution = cfd.solution + for i in domain.ist:(domain.ied) # Python: range(ist, ied+1) → ied+1-1 = ied + j = i - domain.ist + 1 # Julia 1-based + k1 = rec.lmc[i - 1] + k2 = rec.lmc[i] + r1 = (i - 1) - k1 + 1 + r2 = i - k2 + 1 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in 1:rec.spatial_order + solution.q_face_left[j] += q[k1 + m - 1] * rec.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m - 1] * rec.coef[r2, m] + end + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03b/src/reconstructor/factory.jl b/example/1d-linear-convection/weno3/julia/03b/src/reconstructor/factory.jl new file mode 100644 index 00000000..70b0cf9f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03b/src/reconstructor/factory.jl @@ -0,0 +1,38 @@ +# src/reconstructor/factory.jl + +""" +ReconstructorFactory +对标 Python: ReconstructorFactory.create(config, domain) +""" +module ReconstructorFactory + +include("../utils.jl") + +# ✅ 不要用 using ..EnoReconstructor(它们不是模块) +# 直接通过 Main. 引用顶层定义的类型 + +function create(config::Any, domain::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + # 处理 WENO 默认命名(如 Python) + if scheme == "weno" + order = getfield_safe(config, :spatial_order, 5) + scheme = "weno$(order)" + end + + if scheme == "eno" + order = getfield_safe(config, :spatial_order, 3) + return Main.EnoReconstructor(order, domain.ntcells) + elseif scheme == "weno3" + return Main.Weno3Reconstructor() + else + error("不支持的重建格式: $scheme(仅支持 eno/weno3)") + end +end + +end # module ReconstructorFactory + +export ReconstructorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03b/src/reconstructor/weno3.jl b/example/1d-linear-convection/weno3/julia/03b/src/reconstructor/weno3.jl new file mode 100644 index 00000000..2b6fe1ab --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03b/src/reconstructor/weno3.jl @@ -0,0 +1,65 @@ +# julia/reconstructor/weno3.jl +""" +WENO3 重构器(与 reconstructor/weno3.py 完全同构) +""" + +mutable struct Weno3Reconstructor + # 无字段,与 Python 一致 +end + +function reconstruct(rec::Weno3Reconstructor, q::Vector{Float64}, cfd::Any) + domain = cfd.domain + solution = cfd.solution + _reconstruct_left_interfaces(domain, q, solution.q_face_left) + _reconstruct_right_interfaces(domain, q, solution.q_face_right) +end + +function _reconstruct_left_interfaces(domain, u, qL) + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in (domain.ist - 1):(domain.ied - 1) + j = i - (domain.ist - 1) + 1 # ← Julia 1-based: j = i - (ist-1) 对应 Python j = i - (ist-1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = _reconstruct_from_left_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_right_interfaces(domain, u, qR) + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in domain.ist:domain.ied + j = i - domain.ist + 1 # ← Julia 1-based: j = i - ist 对应 Python j = i - ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = _reconstruct_from_right_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_from_left_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -0.5*v1 + 1.5*v2 # r=1 stencil + q1 = 0.5*v2 + 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end + +function _reconstruct_from_right_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 0.5*v1 + 0.5*v2 # r=1 stencil + q1 = 1.5*v2 - 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03b/src/registry.jl b/example/1d-linear-convection/weno3/julia/03b/src/registry.jl new file mode 100644 index 00000000..6acd9aaa --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03b/src/registry.jl @@ -0,0 +1,164 @@ +""" +统一注册系统 + 通用工厂 +- ComponentRegistry: 组件注册表 +- @register_component: 装饰器宏 +- BaseFactory: 通用工厂接口 +""" + +module ComponentRegistry + +# 注册表:Dict{category => Dict{name => constructor}} +const _REGISTRIES = Dict{String, Dict{String, Function}}() +const _VERBOSE = Ref(true) + +# ---------------------- 注册核心逻辑 ---------------------- +""" + register(category::String, name::String, ctor::Function) + +注册一个组件构造函数到指定类别。 +如果已存在同名组件且不同,则发出警告。 +""" +function register(category::String, name::String, ctor::Function) + if !haskey(_REGISTRIES, category) + _REGISTRIES[category] = Dict{String, Function}() + end + + registry = _REGISTRIES[category] + if haskey(registry, name) + if registry[name] !== ctor && _VERBOSE[] + @warn "覆盖注册: $category.$name" + end + end + + registry[name] = ctor + if _VERBOSE[] + println("✅ 已注册: $category.$name -> $(nameof(ctor))") + end +end + +""" + create(category::String, name::String, args...; kwargs...) + +从注册表创建组件实例。 +""" +function create(category::String, name::String, args...; kwargs...) + if !haskey(_REGISTRIES, category) + error("❌ 未知类别: $category (可用: $(collect(keys(_REGISTRIES))))") + end + + registry = _REGISTRIES[category] + lname = lowercase(name) + if !haskey(registry, lname) + available = sort(collect(keys(registry))) + error("❌ 未找到: $category.$name (可用: $available)") + end + + return registry[lname](args...; kwargs...) +end + +""" + list_all() + +返回所有已注册组件(按类别)。 +""" +function list_all() + return Dict(cat => sort(collect(keys(reg))) for (cat, reg) in _REGISTRIES) +end + +""" + set_verbose(flag::Bool) + +开启/关闭注册提示。 +""" +function set_verbose(flag::Bool) + _VERBOSE[] = flag +end + +end # module ComponentRegistry + + +# ---------------------- 装饰器宏:@register_component ---------------------- +""" +@register_component(category, [name]) + +用法: + @register_component("boundary", "periodic") + struct PeriodicBoundary ... + +若省略 name,则使用类型名的小写形式。 +""" +macro register_component(category::String, name_expr) + error("@register_component 需在类型定义前使用,且必须在模块顶层") +end + +macro register_component(category::String) + error("@register_component(category, name) 需指定 name 或在类型后使用") +end + +# 重载:@register_component("category", "name") struct X ... end +macro register_component(category::String, name::String, ex) + if !Meta.isexpr(ex, :struct) + error("@register_component 必须作用于 struct 定义") + end + + struct_name = ex.args[2] + if Meta.isexpr(struct_name, :curly) + struct_name = struct_name.args[1] + end + + # 插入注册调用(在模块顶层) + quote + $(esc(ex)) + $(ComponentRegistry).register($(category), $(name), $(esc(struct_name))) + end +end + +# 重载:@register_component("category") struct X ... end → name = lowercase(nameof(X)) +macro register_component(category::String, ex) + if !Meta.isexpr(ex, :struct) + error("@register_component 必须作用于 struct 定义") + end + + struct_name = ex.args[2] + if Meta.isexpr(struct_name, :curly) + struct_name = struct_name.args[1] + end + + name_str = string(struct_name) |> lowercase + + quote + $(esc(ex)) + $(ComponentRegistry).register($(category), $(name_str), $(esc(struct_name))) + end +end + + +# ---------------------- 通用工厂 ---------------------- +module BaseFactory + +using ..ComponentRegistry + +""" + create_component(category::String, name::String, args...; kwargs...) + +通用工厂接口,与 Python BaseFactory.create_component 行为一致。 +""" +function create_component(category::String, name::String, args...; kwargs...) + return ComponentRegistry.create(category, name, args...; kwargs...) +end + +""" + get_available_components(category::String) + +列出某类别下所有可用组件。 +""" +function get_available_components(category::String) + all = ComponentRegistry.list_all() + return get(all, category, String[]) +end + +end # module BaseFactory + + +# 导出接口 +export ComponentRegistry, BaseFactory, @register_component \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03b/src/residual.jl b/example/1d-linear-convection/weno3/julia/03b/src/residual.jl new file mode 100644 index 00000000..11a88593 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03b/src/residual.jl @@ -0,0 +1,69 @@ +# julia/residual.jl +""" +残差计算器(与 residual.py 完全同构) +- 封装重建→通量→散度完整流程 +- 依赖 cfd 的多个字段 +""" + +include("mesh.jl") + +mutable struct ResidualCalculator + cfd::Any + config::Any + domain::Any + solution::Any + mesh::Mesh + reconstructor::Any + flux_calculator::Any + + function ResidualCalculator(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + mesh = domain.mesh + reconstructor = cfd.reconstructor + + # ✅ 内部创建 flux_calculator(对标 Python) + flux_calculator = FluxCalculatorFactory.create(cfd) + + new(cfd, config, domain, solution, mesh, reconstructor, flux_calculator) + end +end + + +""" +计算完整残差(对外唯一接口) +""" +function compute!(calc::ResidualCalculator) + _reconstruct(calc) + _compute_inviscid_flux(calc) + _compute_flux_divergence(calc) +end + +""" +私有方法:界面值重建 +""" +function _reconstruct(calc::ResidualCalculator) + reconstruct(calc.reconstructor, calc.solution.u, calc.cfd) +end + +""" +私有方法:计算无粘通量 +""" +function _compute_inviscid_flux(calc::ResidualCalculator) + compute!(calc.flux_calculator, + calc.solution.q_face_left, + calc.solution.q_face_right, + calc.solution.flux) +end + +""" +私有方法:计算通量散度(残差 = -dF/dx) +""" +function _compute_flux_divergence(calc::ResidualCalculator) + solution = calc.solution + mesh = calc.mesh + for i in 1:mesh.ncells + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / mesh.dx + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03b/src/solution.jl b/example/1d-linear-convection/weno3/julia/03b/src/solution.jl new file mode 100644 index 00000000..7044c001 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03b/src/solution.jl @@ -0,0 +1,60 @@ +# julia/solution.jl +""" +Solution 结构体(与真实 solution.py 完全同构) +字段顺序、方法、逻辑 1:1 对齐 +""" + +include("mesh.jl") +include("domain.jl") +include("initial_condition.jl") + +mutable struct Solution + domain::Domain + q_face_left::Vector{Float64} + q_face_right::Vector{Float64} + flux::Vector{Float64} + res::Vector{Float64} + u::Vector{Float64} + un::Vector{Float64} + + function Solution(config::Any, domain::Domain) + mesh = domain.mesh + + q_face_left = zeros(Float64, mesh.nnodes) + q_face_right = zeros(Float64, mesh.nnodes) + flux = zeros(Float64, mesh.nnodes) + res = zeros(Float64, mesh.ncells) + u = zeros(Float64, domain.ntcells) + un = zeros(Float64, domain.ntcells) + + sol = new(domain, q_face_left, q_face_right, flux, res, u, un) + initialize_from_config(sol, config) + return sol + end +end + +""" +重置解数组为初始状态 +""" +function reset_solution(sol::Solution) + fill!(sol.u, 0.0) + fill!(sol.un, 0.0) +end + +""" +根据配置初始化场 +""" + +function initialize_from_config(sol::Solution, config::Any) + ic_type = getfield_safe(config, :ic_type, "step") + ic = InitialConditionFactory.create(ic_type, config) + apply(ic, sol) +end + +""" +更新旧场:un = u +""" +function update_old_field(sol::Solution) + sol.un .= sol.u # 等价于 Python 的 un[:] = u[:] +end + diff --git a/example/1d-linear-convection/weno3/julia/03b/src/solver.jl b/example/1d-linear-convection/weno3/julia/03b/src/solver.jl new file mode 100644 index 00000000..50ae55db --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03b/src/solver.jl @@ -0,0 +1,130 @@ +# julia/solver.jl +""" +CFD 求解器主类(与 solver.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("initial_condition.jl") +include("boundary.jl") +include("flux/flux.jl") +include("residual.jl") +include("time_integration.jl") +include("reconstructor/eno.jl") +include("reconstructor/weno3.jl") +include("reconstructor/factory.jl") + +# 导入工厂模块(必须在顶层!) +using .FluxCalculatorFactory +using .TimeIntegratorFactory +using .BoundaryConditionFactory +using .ReconstructorFactory + +# ---------------------- Cfd 求解器 ---------------------- +mutable struct Cfd + config::Any + domain::Domain + solution::Solution + reconstructor::Any + residual_calculator::ResidualCalculator + integrator::Any + boundary_condition::Any + result::Dict{String, Any} + + function Cfd(config::Any, mesh::Mesh) + domain = Domain(config, mesh) + solution = Solution(config, domain) + reconstructor = ReconstructorFactory.create(config, domain) + + # 1. 初始上下文(仅包含不依赖其他组件的字段) + full_cfd = ( + config = config, + domain = domain, + solution = solution, + reconstructor = reconstructor + # 注意:不预先占位 nothing! + ) + + # 2. 创建 boundary_condition(只依赖 config + domain) + boundary_condition = BoundaryConditionFactory.create(full_cfd) + full_cfd = merge(full_cfd, (boundary_condition = boundary_condition,)) + + # 3. 创建 residual_calculator(依赖上面所有字段) + residual_calculator = ResidualCalculator(full_cfd) + full_cfd = merge(full_cfd, (residual_calculator = residual_calculator,)) + + # 4. 创建 integrator(依赖 residual_calculator 等) + integrator = TimeIntegratorFactory.create(full_cfd) + full_cfd = merge(full_cfd, (integrator = integrator,)) + + # 5. 注入完整 self 到组件(确保它们能访问彼此) + residual_calculator.cfd = full_cfd + integrator.base.cfd = full_cfd + + result = Dict{String, Any}() + new(config, domain, solution, reconstructor, residual_calculator, integrator, boundary_condition, result) + end +end + +""" +通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界 +""" +function exact_solution(cfd::Cfd) + x = cfd.domain.mesh.xcc + T = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = @. (x - c * T + L) % L + + # ✅ 使用工厂创建初始条件(对标 Python) + ic = InitialConditionFactory.create(cfd.config.ic_type, cfd.config) + + return evaluate_at(ic, x_shifted) +end + +""" +主求解循环 +""" +function run!(cfd::Cfd) + # 应用初始边界条件并同步 old field + apply!(cfd.boundary_condition, cfd.solution.u) + update_old_field(cfd.solution) + + t = 0.0 + dt_old = cfd.config.dt + dt = dt_old + + while t < cfd.config.final_time + if t + dt > cfd.config.final_time + dt = cfd.config.final_time - t + end + #@show t, dt, maximum(cfd.solution.u), minimum(cfd.solution.u) + # 执行时间步 + step(cfd.integrator, dt) + t += dt + end + + # 恢复 dt + cfd.config.dt = dt_old + + # 整理结果 + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied-1] # Python: [ist:ied] + analytical = exact_solution(cfd) + + cfd.result = Dict( + "x" => cfd.domain.mesh.xcc, + "numerical" => u_numerical, + "analytical" => analytical, + "config" => Dict( + "scheme" => cfd.config.recon_scheme, + "order" => cfd.config.spatial_order, + "rk_order" => cfd.config.rk_order, + "final_time" => cfd.config.final_time + ) + ) + + return u_numerical +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03b/src/time_integration.jl b/example/1d-linear-convection/weno3/julia/03b/src/time_integration.jl new file mode 100644 index 00000000..add564ba --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03b/src/time_integration.jl @@ -0,0 +1,169 @@ +# julia/time_integration.jl +""" +时间推进器模块(与 time_integration.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("residual.jl") +include("boundary.jl") + +# ---------------------- 抽象时间推进器基类 ---------------------- +abstract type TimeIntegrator end + +mutable struct TimeIntegratorBase <: TimeIntegrator + cfd::Any + config::Any + domain::Domain + solution::Solution + residual_calculator::Any # ResidualCalculator +end + +function TimeIntegratorBase(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + residual_calculator = cfd.residual_calculator + TimeIntegratorBase(cfd, config, domain, solution, residual_calculator) +end + +function compute_residual(integrator::TimeIntegratorBase) + compute!(integrator.residual_calculator) +end + +function apply_boundary(integrator::TimeIntegratorBase) + apply!(integrator.cfd.boundary_condition, integrator.solution.u) +end + +function map_idx(integrator::TimeIntegratorBase, i::Int) + return i - integrator.domain.ist + 1 # ← +1 转为 1-based +end + +# ---------------------- RK1Integrator ---------------------- +mutable struct RK1Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK1Integrator(cfd::Any) + RK1Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK1Integrator, dt::Float64) + compute_residual(integrator.base) + for i in integrator.base.domain.ist:(integrator.base.domain.ied - 1) + j = map_idx(integrator.base, i) + integrator.base.solution.u[i] += dt * integrator.base.solution.res[j] + end + apply_boundary(integrator.base) + update_old_field(integrator.base.solution) +end + +# ---------------------- RK2Integrator ---------------------- +mutable struct RK2Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK2Integrator(cfd::Any) + RK2Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK2Integrator, dt::Float64) + base = integrator.base + # 阶段1:预测步 + compute_residual(base) + u_pred = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u_pred[i] += dt * base.solution.res[j] + end + base.solution.u .= u_pred + apply_boundary(base) + # 阶段2:校正步 + compute_residual(base) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = 0.5 * base.solution.un[i] + 0.5 * base.solution.u[i] + 0.5 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- RK3Integrator ---------------------- +mutable struct RK3Integrator <: TimeIntegrator + base::TimeIntegratorBase +end + +function RK3Integrator(cfd::Any) + RK3Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK3Integrator, dt::Float64) + base = integrator.base + # 阶段1 + compute_residual(base) + u1 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u1[i] += dt * base.solution.res[j] + end + base.solution.u .= u1 + apply_boundary(base) + # 阶段2 + compute_residual(base) + u2 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u2[i] = 0.75 * base.solution.un[i] + 0.25 * base.solution.u[i] + 0.25 * dt * base.solution.res[j] + end + base.solution.u .= u2 + apply_boundary(base) + # 阶段3 + compute_residual(base) + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = c1 * base.solution.un[i] + c2 * base.solution.u[i] + c3 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end + +# ---------------------- TimeIntegratorFactory ---------------------- +module TimeIntegratorFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "时间积分器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + rk_order = cfd.config.rk_order + if !(rk_order isa Integer) || rk_order < 1 + error("rk_order 必须为正整数,当前值: $rk_order") + end + + name = "rk$rk_order" + if !haskey(_REGISTRY, name) + available = sort(collect(keys(_REGISTRY))) + error("未注册的时间积分器: '$name'。可用选项: $available") + end + + return _REGISTRY[name](cfd) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("rk1", cfd -> Main.RK1Integrator(cfd)) +register("rk2", cfd -> Main.RK2Integrator(cfd)) +register("rk3", cfd -> Main.RK3Integrator(cfd)) + +end # module TimeIntegratorFactory + +export TimeIntegratorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03b/src/utils.jl b/example/1d-linear-convection/weno3/julia/03b/src/utils.jl new file mode 100644 index 00000000..3786cd1a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03b/src/utils.jl @@ -0,0 +1,9 @@ +# src/utils.jl + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end diff --git a/example/1d-linear-convection/weno3/julia/03c/examples/run_eno_weno.jl b/example/1d-linear-convection/weno3/julia/03c/examples/run_eno_weno.jl new file mode 100644 index 00000000..da24e6cd --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/examples/run_eno_weno.jl @@ -0,0 +1,50 @@ +# examples/run_eno_weno.jl +""" +1:1 复刻 run_eno_weno.py 的 Julia 版本 +""" + +include("../src/config.jl") +include("../src/mesh.jl") +include("../src/solver.jl") +include("../src/plotter.jl") + +function performEnoWenoAnalysis() + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + println("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + with_reconstruction(config_eno3, "eno", 3) # 显式指定 3 阶 + config_eno3.dt = 0.0025 # 覆盖默认值 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + run!(cfd_eno3) # 求解并生成 result 字典 + + # 3. 配置并运行 WENO3 求解 + println("Running WENO3 solver...") + config_weno3 = CfdConfig() + with_reconstruction(config_weno3, "weno", 3) # 显式指定 3 阶(WENO 默认 5 阶) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + run!(cfd_weno3) + + # 5. 绘制 ENO/WENO 对比图 + println("Plotting comparison results...") + plot_eno_weno_comparison( + cfd_eno3.result, + cfd_weno3.result; + save_path="eno_weno_comparison.png" + ) + + return cfd_eno3, cfd_weno3 +end + +# 主程序入口 +if abspath(PROGRAM_FILE) == @__FILE__ + performEnoWenoAnalysis() + println("Analysis completed!") +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03c/src/boundary.jl b/example/1d-linear-convection/weno3/julia/03c/src/boundary.jl new file mode 100644 index 00000000..82f58b71 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/boundary.jl @@ -0,0 +1,121 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +#using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end + + +# ---------------------- BoundaryConditionFactory ---------------------- +module BoundaryConditionFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "边界条件 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + bc_type = cfd.config.boundary_type + if !(bc_type isa AbstractString) + error("boundary_type 必须为字符串,当前值: $bc_type") + end + + if !haskey(_REGISTRY, bc_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的边界条件: '$bc_type'。可用选项: $available") + end + + return _REGISTRY[bc_type](cfd) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("periodic", cfd -> Main.PeriodicBoundary(cfd)) +register("dirichlet", cfd -> Main.DirichletBoundary(cfd)) +register("neumann", cfd -> Main.NeumannBoundary(cfd)) + +end # module BoundaryConditionFactory + +export BoundaryConditionFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03c/src/config.jl b/example/1d-linear-convection/weno3/julia/03c/src/config.jl new file mode 100644 index 00000000..dd34fe7a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/config.jl @@ -0,0 +1,69 @@ +# julia/config.jl +""" +CfdConfig:与 Python config.py 完全同构 +""" +mutable struct CfdConfig + ic_type::String + recon_scheme::String + flux_type::String + rk_order::Int + wave_speed::Float64 + final_time::Float64 + dt::Float64 + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + spatial_order::Int + + function CfdConfig() + new( + "step", + "eno", + "rusanov", + 1, + 1.0, + 0.625, + 0.025, + "periodic", + 1.0, + 2.0, + 2 + ) + end +end + +""" +专用配置:重建方案(链式调用) +""" +function with_reconstruction(cfg::CfdConfig, scheme::String, order::Union{Int, Nothing}=nothing) + cfg.recon_scheme = lowercase(scheme) + + if order !== nothing + cfg.spatial_order = order + else + if startswith(cfg.recon_scheme, "weno") + cfg.spatial_order = 5 + elseif cfg.recon_scheme == "eno" + cfg.spatial_order = 3 + else + error("不支持的重建格式:$scheme(仅支持 eno/weno)") + end + end + + return cfg # 支持链式调用 +end + +""" +专用配置:边界条件(链式调用) +""" +function with_boundary(cfg::CfdConfig, bc_type::String; left_value=nothing, right_value=nothing) + cfg.boundary_type = bc_type + if left_value !== nothing + cfg.left_boundary_value = left_value + end + if right_value !== nothing + cfg.right_boundary_value = right_value + end + return cfg +end + diff --git a/example/1d-linear-convection/weno3/julia/03c/src/domain.jl b/example/1d-linear-convection/weno3/julia/03c/src/domain.jl new file mode 100644 index 00000000..eec26114 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/domain.jl @@ -0,0 +1,58 @@ +# julia/domain.jl +""" +Domain 结构体(与 domain.py 完全同构) +- 保存 config +- nghosts 计算逻辑 1:1 +- ist/ied 为 0-based(与 Python 一致) +""" + +include("mesh.jl") +include("utils.jl") + +mutable struct Domain + config::Any # 保存 config(与 Python 一致) + mesh::Mesh + nghosts::Int + ist::Int # 0-based + ied::Int # 0-based + ntcells::Int +end + +function Domain(config::Any, mesh::Mesh) + nghosts = _calc_nghosts(config) + ist = nghosts + 1 # ← 1-based 起始索引 + ied = ist + mesh.ncells # ← 1-based 结束索引(不包含) + ntcells = mesh.ncells + 2 * nghosts + Domain(config, mesh, nghosts, ist, ied, ntcells) +end + +function _calc_nghosts(config::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + order = getfield_safe(config, :spatial_order, 2) + + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + if scheme == "eno" + nghosts = order + elseif startswith(scheme, "weno") + nghosts = order ÷ 2 + 1 + else + error("未知重建格式 $(scheme),无法计算ghost层!") + end + + if nghosts <= 0 + error("计算得到的ghost层数量无效:$(nghosts)(阶数$(order),格式$(scheme))") + end + + return nghosts +end + +function is_physical_cell(domain::Domain, idx::Int) + return domain.ist <= idx < domain.ied +end + +function get_physical_indices(domain::Domain) + return domain.ist:(domain.ied - 1) +end diff --git a/example/1d-linear-convection/weno3/julia/03c/src/flux/base.jl b/example/1d-linear-convection/weno3/julia/03c/src/flux/base.jl new file mode 100644 index 00000000..58dd6cc9 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/flux/base.jl @@ -0,0 +1,7 @@ +# src/flux/base.jl +""" +抽象通量计算器接口 +Julia 无 ABC,用文档约定: +- 所有子类型必须实现 `compute!(calc, qL, qR, flux)` +""" +abstract type InviscidFluxCalculator end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03c/src/flux/engquist_osher.jl b/example/1d-linear-convection/weno3/julia/03c/src/flux/engquist_osher.jl new file mode 100644 index 00000000..c0de183d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/flux/engquist_osher.jl @@ -0,0 +1,25 @@ +# src/flux/engquist_osher.jl +mutable struct EngquistOsherFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function EngquistOsherFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::EngquistOsherFluxCalculator, qL::Vector{Float64}, qR::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + c = calc.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = qL[i] + u_R = qR[i] + flux[i] = cp * u_L + cm * u_R + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03c/src/flux/factory.jl b/example/1d-linear-convection/weno3/julia/03c/src/flux/factory.jl new file mode 100644 index 00000000..6ae4917d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/flux/factory.jl @@ -0,0 +1,26 @@ +# src/flux/factory.jl + +module FluxCalculatorFactory + +# 全局注册表 +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "通量计算器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + flux_type = cfd.config.flux_type + if !haskey(_REGISTRY, flux_type) + error("未注册的通量计算器: '$flux_type'。可用: $(keys(_REGISTRY))") + end + return _REGISTRY[flux_type](cfd) +end + +register("rusanov", cfd -> Main.RusanovFluxCalculator(cfd)) +register("engquist-osher", cfd -> Main.EngquistOsherFluxCalculator(cfd)) + +end # module FluxCalculatorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03c/src/flux/flux.jl b/example/1d-linear-convection/weno3/julia/03c/src/flux/flux.jl new file mode 100644 index 00000000..533f034c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/flux/flux.jl @@ -0,0 +1,10 @@ +# src/flux/flux.jl + +# 加载组件(顺序很重要!) +include("base.jl") +include("rusanov.jl") +include("engquist_osher.jl") +include("factory.jl") + +# 导出(如果你未来用模块) +# export InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03c/src/flux/rusanov.jl b/example/1d-linear-convection/weno3/julia/03c/src/flux/rusanov.jl new file mode 100644 index 00000000..70c20fc3 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/flux/rusanov.jl @@ -0,0 +1,28 @@ +# src/flux/rusanov.jl + +mutable struct RusanovFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function RusanovFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::RusanovFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = calc.wave_speed + c_R = calc.wave_speed + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03c/src/initial_condition.jl b/example/1d-linear-convection/weno3/julia/03c/src/initial_condition.jl new file mode 100644 index 00000000..1b9993aa --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/initial_condition.jl @@ -0,0 +1,112 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end + +# ---------------------- InitialConditionFactory ---------------------- +module InitialConditionFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "初始条件 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(ic_type::String, config::Any) + if !(ic_type isa AbstractString) + error("ic_type 必须为字符串,当前值: $ic_type") + end + + if !haskey(_REGISTRY, ic_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的初始条件: '$ic_type'。可用选项: $available") + end + + return _REGISTRY[ic_type](config) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("step", config -> Main.StepFunctionIC(config)) +register("sin", config -> Main.SineWaveIC(config)) +register("gaussian", config -> Main.GaussianPulseIC(config)) + +end # module InitialConditionFactory + +export InitialConditionFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03c/src/mesh.jl b/example/1d-linear-convection/weno3/julia/03c/src/mesh.jl new file mode 100644 index 00000000..1b0dd4bf --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/mesh.jl @@ -0,0 +1,45 @@ +# julia/mesh.jl +""" +Mesh 结构体(与提供的 mesh.py 完全同构) +- 字段名、初始化顺序、循环逻辑完全一致 +- 不做任何泛化或优化 +""" + +mutable struct Mesh + xmin::Float64 + xmax::Float64 + ncells::Int + nnodes::Int + nx::Int + x::Vector{Float64} + xcc::Vector{Float64} + L::Float64 # 在 init_mesh 中赋值 + dx::Float64 # 在 init_mesh 中赋值 + + function Mesh() + # 与 Python __init__ 完全一致 + xmin = 0.0 + xmax = 2.0 + ncells = 40 + nnodes = ncells + 1 + nx = ncells + x = zeros(Float64, nnodes) + xcc = zeros(Float64, ncells) + + # 调用 init_mesh(模拟 Python) + L = xmax - xmin + dx = L / ncells + + # Generate node coordinates: for i in range(self.nnodes) + for i in 1:nnodes + x[i] = xmin + (i - 1) * dx # Python i → Julia i-1 + end + + # Generate cell center coordinates + for i in 1:ncells + xcc[i] = 0.5 * (x[i] + x[i+1]) + end + + new(xmin, xmax, ncells, nnodes, nx, x, xcc, L, dx) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03c/src/plotter.jl b/example/1d-linear-convection/weno3/julia/03c/src/plotter.jl new file mode 100644 index 00000000..a77d8d68 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/plotter.jl @@ -0,0 +1,147 @@ +# julia/plotter.jl +""" +CFDPlotter 的 Julia 实现(通过 PythonCall.jl 调用 Matplotlib) +确保与 Python plotter.py 行为完全一致 +""" + +using PythonCall + +# 初始化 Python 环境(加载 matplotlib, inflect) +const plt = pyimport("matplotlib.pyplot") +const inflect = pyimport("inflect") + +mutable struct CFDPlotter + default_styles::Dict{String, Any} + p::Py +end + +function CFDPlotter() + default_styles = Dict{String, Any}( + "numerical" => Dict( + :color => "blue", + :linestyle => "-", + :marker => "o", + :markerfacecolor => "none" + ), + "analytical" => Dict( + :color => "red", + :linestyle => "--", + :marker => "", + :linewidth => 1.5 + ), + "comparison" => [ + Dict(:color => "black", :linestyle => "-", :marker => "o", :markerfacecolor => "none"), + Dict(:color => "blue", :linestyle => "--", :marker => "s", :markerfacecolor => "none"), + Dict(:color => "green", :linestyle => ":", :marker => "^", :markerfacecolor => "none") + ] + ) + p = inflect.engine() + CFDPlotter(default_styles, p) +end + +""" +轻量即时绘图(快速验证结果) +""" +function plot_quick(plotter::CFDPlotter, cfd_result::Dict; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + rk_order = cfd_result["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = cfd_result["config"]["final_time"] + order = cfd_result["config"]["order"] + scheme = uppercase(cfd_result["config"]["scheme"]) + actual_title = "1D Convection (t=$(final_time))\n$(order)th-order $(scheme) + $(rk_str)-order RK" + end + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"]; + label="Numerical ($(uppercase(cfd_result["config"]["scheme"])))", + plotter.default_styles["numerical"]..., + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + # 通用样式 + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +""" +多格式/多精度对比绘图 +""" +function plot_comparison(plotter::CFDPlotter, result_list::Vector{Dict{String, Any}}; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + schemes = [uppercase(r["config"]["scheme"]) * string(r["config"]["order"]) for r in result_list] + rk_order = result_list[1]["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = result_list[1]["config"]["final_time"] + actual_title = "1D Convection Comparison (t=$(final_time))\n$(join(schemes, ", ")) + $(rk_str)-order RK" + end + + # 绘制多个数值解 + for (i, res) in enumerate(result_list) + style = plotter.default_styles["comparison"][mod1(i, length(plotter.default_styles["comparison"]))] + label = "Numerical ($(uppercase(res["config"]["scheme"]))$(res["config"]["order"]))" + plt.plot( + res["x"], res["numerical"]; + label=label, + style..., + markersize=5, linewidth=0.5 + ) + end + + # 绘制解析解 + plt.plot( + result_list[1]["x"], result_list[1]["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +function _set_common_style(plotter::CFDPlotter, title::String) + plt.title(title, fontsize=12) + plt.xlabel("x", fontsize=10) + plt.ylabel("u", fontsize=10) + plt.legend(fontsize=9) + plt.grid(true, color="gray", linestyle="--", linewidth=0.5, alpha=0.7) + plt.tight_layout() +end + +""" +快捷函数:ENO/WENO对比绘图 +""" +function plot_eno_weno_comparison(eno_result::Dict, weno_result::Dict; save_path=nothing) + plotter = CFDPlotter() + plot_comparison(plotter, [eno_result, weno_result]; save_path=save_path) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03c/src/reconstructor/eno.jl b/example/1d-linear-convection/weno3/julia/03c/src/reconstructor/eno.jl new file mode 100644 index 00000000..e78a636f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/reconstructor/eno.jl @@ -0,0 +1,107 @@ +# julia/reconstructor/eno.jl +""" +ENO 重构器(与 reconstructor/eno.py 完全同构) +""" + +# ---------------------- ENO 系数初始化 ---------------------- +function _init_eno_coef!(spatial_order::Int, coef::Matrix{Float64}) + if spatial_order == 1 + coef[1, 1] = 1.0 + coef[2, 1] = 1.0 + elseif spatial_order == 2 + coef[1, 1:2] = [3.0/2.0, -1.0/2.0] + coef[2, 1:2] = [1.0/2.0, 1.0/2.0] + coef[3, 1:2] = [-1.0/2.0, 3.0/2.0] + elseif spatial_order == 3 + coef[1, 1:3] = [11.0/6.0, -7.0/6.0, 1.0/3.0] + coef[2, 1:3] = [1.0/3.0, 5.0/6.0, -1.0/6.0] + coef[3, 1:3] = [-1.0/6.0, 5.0/6.0, 1.0/3.0] + coef[4, 1:3] = [1.0/3.0, -7.0/6.0, 11.0/6.0] + elseif spatial_order == 4 + coef[1, 1:4] = [25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0] + coef[2, 1:4] = [1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0] + coef[3, 1:4] = [-1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0] + coef[4, 1:4] = [1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0] + coef[5, 1:4] = [-1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0] + elseif spatial_order == 5 + coef[1, 1:5] = [137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0] + coef[2, 1:5] = [1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0] + coef[3, 1:5] = [-1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0] + coef[4, 1:5] = [1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0] + coef[5, 1:5] = [-1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0] + coef[6, 1:5] = [1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0] + elseif spatial_order == 6 + coef[1, 1:6] = [49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0] + coef[2, 1:6] = [1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0] + coef[3, 1:6] = [-1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0] + coef[4, 1:6] = [1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0] + coef[5, 1:6] = [-1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0] + coef[6, 1:6] = [1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0] + coef[7, 1:6] = [-1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0] + elseif spatial_order == 7 + coef[1, 1:7] = [363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0] + coef[2, 1:7] = [1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0] + coef[3, 1:7] = [-1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0] + coef[4, 1:7] = [1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0] + coef[5, 1:7] = [-1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0] + coef[6, 1:7] = [1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0] + coef[7, 1:7] = [-1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0] + coef[8, 1:7] = [1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0] + else + error("ENO 系数未实现 order=$spatial_order") + end +end + +# ---------------------- ENO 重构器 ---------------------- +mutable struct EnoReconstructor + spatial_order::Int + ntcells::Int + lmc::Vector{Int} + coef::Matrix{Float64} + dd::Matrix{Float64} + + function EnoReconstructor(spatial_order::Int, ntcells::Int) + lmc = zeros(Int, ntcells) + coef = zeros(Float64, spatial_order + 1, spatial_order) + dd = zeros(Float64, spatial_order, ntcells) + _init_eno_coef!(spatial_order, coef) + new(spatial_order, ntcells, lmc, coef, dd) + end +end + +function reconstruct(rec::EnoReconstructor, q::Vector{Float64}, cfd::Any) + # 1. 差商计算 (dd[1,:] = q) + @views rec.dd[1, :] .= q + for m in 2:rec.spatial_order + for j in 1:(rec.ntcells - m + 1) + rec.dd[m, j] = rec.dd[m-1, j+1] - rec.dd[m-1, j] + end + end + + # 2. 选择 smoothest stencil + domain = cfd.domain + for i in (domain.ist - 1):(domain.ied) # Python: range(ist-1, ied+1) → ied+1-1 = ied + rec.lmc[i] = i + for m in 2:rec.spatial_order + if abs(rec.dd[m, rec.lmc[i] - 1]) < abs(rec.dd[m, rec.lmc[i]]) + rec.lmc[i] -= 1 + end + end + end + + # 3. 重构界面值 + solution = cfd.solution + for i in domain.ist:(domain.ied) # Python: range(ist, ied+1) → ied+1-1 = ied + j = i - domain.ist + 1 # Julia 1-based + k1 = rec.lmc[i - 1] + k2 = rec.lmc[i] + r1 = (i - 1) - k1 + 1 + r2 = i - k2 + 1 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in 1:rec.spatial_order + solution.q_face_left[j] += q[k1 + m - 1] * rec.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m - 1] * rec.coef[r2, m] + end + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03c/src/reconstructor/factory.jl b/example/1d-linear-convection/weno3/julia/03c/src/reconstructor/factory.jl new file mode 100644 index 00000000..70b0cf9f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/reconstructor/factory.jl @@ -0,0 +1,38 @@ +# src/reconstructor/factory.jl + +""" +ReconstructorFactory +对标 Python: ReconstructorFactory.create(config, domain) +""" +module ReconstructorFactory + +include("../utils.jl") + +# ✅ 不要用 using ..EnoReconstructor(它们不是模块) +# 直接通过 Main. 引用顶层定义的类型 + +function create(config::Any, domain::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + # 处理 WENO 默认命名(如 Python) + if scheme == "weno" + order = getfield_safe(config, :spatial_order, 5) + scheme = "weno$(order)" + end + + if scheme == "eno" + order = getfield_safe(config, :spatial_order, 3) + return Main.EnoReconstructor(order, domain.ntcells) + elseif scheme == "weno3" + return Main.Weno3Reconstructor() + else + error("不支持的重建格式: $scheme(仅支持 eno/weno3)") + end +end + +end # module ReconstructorFactory + +export ReconstructorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03c/src/reconstructor/weno3.jl b/example/1d-linear-convection/weno3/julia/03c/src/reconstructor/weno3.jl new file mode 100644 index 00000000..2b6fe1ab --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/reconstructor/weno3.jl @@ -0,0 +1,65 @@ +# julia/reconstructor/weno3.jl +""" +WENO3 重构器(与 reconstructor/weno3.py 完全同构) +""" + +mutable struct Weno3Reconstructor + # 无字段,与 Python 一致 +end + +function reconstruct(rec::Weno3Reconstructor, q::Vector{Float64}, cfd::Any) + domain = cfd.domain + solution = cfd.solution + _reconstruct_left_interfaces(domain, q, solution.q_face_left) + _reconstruct_right_interfaces(domain, q, solution.q_face_right) +end + +function _reconstruct_left_interfaces(domain, u, qL) + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in (domain.ist - 1):(domain.ied - 1) + j = i - (domain.ist - 1) + 1 # ← Julia 1-based: j = i - (ist-1) 对应 Python j = i - (ist-1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = _reconstruct_from_left_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_right_interfaces(domain, u, qR) + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in domain.ist:domain.ied + j = i - domain.ist + 1 # ← Julia 1-based: j = i - ist 对应 Python j = i - ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = _reconstruct_from_right_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_from_left_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -0.5*v1 + 1.5*v2 # r=1 stencil + q1 = 0.5*v2 + 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end + +function _reconstruct_from_right_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 0.5*v1 + 0.5*v2 # r=1 stencil + q1 = 1.5*v2 - 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03c/src/registry.jl b/example/1d-linear-convection/weno3/julia/03c/src/registry.jl new file mode 100644 index 00000000..6acd9aaa --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/registry.jl @@ -0,0 +1,164 @@ +""" +统一注册系统 + 通用工厂 +- ComponentRegistry: 组件注册表 +- @register_component: 装饰器宏 +- BaseFactory: 通用工厂接口 +""" + +module ComponentRegistry + +# 注册表:Dict{category => Dict{name => constructor}} +const _REGISTRIES = Dict{String, Dict{String, Function}}() +const _VERBOSE = Ref(true) + +# ---------------------- 注册核心逻辑 ---------------------- +""" + register(category::String, name::String, ctor::Function) + +注册一个组件构造函数到指定类别。 +如果已存在同名组件且不同,则发出警告。 +""" +function register(category::String, name::String, ctor::Function) + if !haskey(_REGISTRIES, category) + _REGISTRIES[category] = Dict{String, Function}() + end + + registry = _REGISTRIES[category] + if haskey(registry, name) + if registry[name] !== ctor && _VERBOSE[] + @warn "覆盖注册: $category.$name" + end + end + + registry[name] = ctor + if _VERBOSE[] + println("✅ 已注册: $category.$name -> $(nameof(ctor))") + end +end + +""" + create(category::String, name::String, args...; kwargs...) + +从注册表创建组件实例。 +""" +function create(category::String, name::String, args...; kwargs...) + if !haskey(_REGISTRIES, category) + error("❌ 未知类别: $category (可用: $(collect(keys(_REGISTRIES))))") + end + + registry = _REGISTRIES[category] + lname = lowercase(name) + if !haskey(registry, lname) + available = sort(collect(keys(registry))) + error("❌ 未找到: $category.$name (可用: $available)") + end + + return registry[lname](args...; kwargs...) +end + +""" + list_all() + +返回所有已注册组件(按类别)。 +""" +function list_all() + return Dict(cat => sort(collect(keys(reg))) for (cat, reg) in _REGISTRIES) +end + +""" + set_verbose(flag::Bool) + +开启/关闭注册提示。 +""" +function set_verbose(flag::Bool) + _VERBOSE[] = flag +end + +end # module ComponentRegistry + + +# ---------------------- 装饰器宏:@register_component ---------------------- +""" +@register_component(category, [name]) + +用法: + @register_component("boundary", "periodic") + struct PeriodicBoundary ... + +若省略 name,则使用类型名的小写形式。 +""" +macro register_component(category::String, name_expr) + error("@register_component 需在类型定义前使用,且必须在模块顶层") +end + +macro register_component(category::String) + error("@register_component(category, name) 需指定 name 或在类型后使用") +end + +# 重载:@register_component("category", "name") struct X ... end +macro register_component(category::String, name::String, ex) + if !Meta.isexpr(ex, :struct) + error("@register_component 必须作用于 struct 定义") + end + + struct_name = ex.args[2] + if Meta.isexpr(struct_name, :curly) + struct_name = struct_name.args[1] + end + + # 插入注册调用(在模块顶层) + quote + $(esc(ex)) + $(ComponentRegistry).register($(category), $(name), $(esc(struct_name))) + end +end + +# 重载:@register_component("category") struct X ... end → name = lowercase(nameof(X)) +macro register_component(category::String, ex) + if !Meta.isexpr(ex, :struct) + error("@register_component 必须作用于 struct 定义") + end + + struct_name = ex.args[2] + if Meta.isexpr(struct_name, :curly) + struct_name = struct_name.args[1] + end + + name_str = string(struct_name) |> lowercase + + quote + $(esc(ex)) + $(ComponentRegistry).register($(category), $(name_str), $(esc(struct_name))) + end +end + + +# ---------------------- 通用工厂 ---------------------- +module BaseFactory + +using ..ComponentRegistry + +""" + create_component(category::String, name::String, args...; kwargs...) + +通用工厂接口,与 Python BaseFactory.create_component 行为一致。 +""" +function create_component(category::String, name::String, args...; kwargs...) + return ComponentRegistry.create(category, name, args...; kwargs...) +end + +""" + get_available_components(category::String) + +列出某类别下所有可用组件。 +""" +function get_available_components(category::String) + all = ComponentRegistry.list_all() + return get(all, category, String[]) +end + +end # module BaseFactory + + +# 导出接口 +export ComponentRegistry, BaseFactory, @register_component \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03c/src/residual.jl b/example/1d-linear-convection/weno3/julia/03c/src/residual.jl new file mode 100644 index 00000000..11a88593 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/residual.jl @@ -0,0 +1,69 @@ +# julia/residual.jl +""" +残差计算器(与 residual.py 完全同构) +- 封装重建→通量→散度完整流程 +- 依赖 cfd 的多个字段 +""" + +include("mesh.jl") + +mutable struct ResidualCalculator + cfd::Any + config::Any + domain::Any + solution::Any + mesh::Mesh + reconstructor::Any + flux_calculator::Any + + function ResidualCalculator(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + mesh = domain.mesh + reconstructor = cfd.reconstructor + + # ✅ 内部创建 flux_calculator(对标 Python) + flux_calculator = FluxCalculatorFactory.create(cfd) + + new(cfd, config, domain, solution, mesh, reconstructor, flux_calculator) + end +end + + +""" +计算完整残差(对外唯一接口) +""" +function compute!(calc::ResidualCalculator) + _reconstruct(calc) + _compute_inviscid_flux(calc) + _compute_flux_divergence(calc) +end + +""" +私有方法:界面值重建 +""" +function _reconstruct(calc::ResidualCalculator) + reconstruct(calc.reconstructor, calc.solution.u, calc.cfd) +end + +""" +私有方法:计算无粘通量 +""" +function _compute_inviscid_flux(calc::ResidualCalculator) + compute!(calc.flux_calculator, + calc.solution.q_face_left, + calc.solution.q_face_right, + calc.solution.flux) +end + +""" +私有方法:计算通量散度(残差 = -dF/dx) +""" +function _compute_flux_divergence(calc::ResidualCalculator) + solution = calc.solution + mesh = calc.mesh + for i in 1:mesh.ncells + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / mesh.dx + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03c/src/solution.jl b/example/1d-linear-convection/weno3/julia/03c/src/solution.jl new file mode 100644 index 00000000..7044c001 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/solution.jl @@ -0,0 +1,60 @@ +# julia/solution.jl +""" +Solution 结构体(与真实 solution.py 完全同构) +字段顺序、方法、逻辑 1:1 对齐 +""" + +include("mesh.jl") +include("domain.jl") +include("initial_condition.jl") + +mutable struct Solution + domain::Domain + q_face_left::Vector{Float64} + q_face_right::Vector{Float64} + flux::Vector{Float64} + res::Vector{Float64} + u::Vector{Float64} + un::Vector{Float64} + + function Solution(config::Any, domain::Domain) + mesh = domain.mesh + + q_face_left = zeros(Float64, mesh.nnodes) + q_face_right = zeros(Float64, mesh.nnodes) + flux = zeros(Float64, mesh.nnodes) + res = zeros(Float64, mesh.ncells) + u = zeros(Float64, domain.ntcells) + un = zeros(Float64, domain.ntcells) + + sol = new(domain, q_face_left, q_face_right, flux, res, u, un) + initialize_from_config(sol, config) + return sol + end +end + +""" +重置解数组为初始状态 +""" +function reset_solution(sol::Solution) + fill!(sol.u, 0.0) + fill!(sol.un, 0.0) +end + +""" +根据配置初始化场 +""" + +function initialize_from_config(sol::Solution, config::Any) + ic_type = getfield_safe(config, :ic_type, "step") + ic = InitialConditionFactory.create(ic_type, config) + apply(ic, sol) +end + +""" +更新旧场:un = u +""" +function update_old_field(sol::Solution) + sol.un .= sol.u # 等价于 Python 的 un[:] = u[:] +end + diff --git a/example/1d-linear-convection/weno3/julia/03c/src/solver.jl b/example/1d-linear-convection/weno3/julia/03c/src/solver.jl new file mode 100644 index 00000000..aeca9515 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/solver.jl @@ -0,0 +1,129 @@ +# julia/solver.jl +""" +CFD 求解器主类(与 solver.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("initial_condition.jl") +include("boundary.jl") +include("flux/flux.jl") +include("residual.jl") +include("time_integration/time_integration.jl") +include("reconstructor/eno.jl") +include("reconstructor/weno3.jl") +include("reconstructor/factory.jl") + +# 导入工厂模块(必须在顶层!) +using .FluxCalculatorFactory +using .BoundaryConditionFactory +using .ReconstructorFactory + +# ---------------------- Cfd 求解器 ---------------------- +mutable struct Cfd + config::Any + domain::Domain + solution::Solution + reconstructor::Any + residual_calculator::ResidualCalculator + integrator::Any + boundary_condition::Any + result::Dict{String, Any} + + function Cfd(config::Any, mesh::Mesh) + domain = Domain(config, mesh) + solution = Solution(config, domain) + reconstructor = ReconstructorFactory.create(config, domain) + + # 1. 初始上下文(仅包含不依赖其他组件的字段) + full_cfd = ( + config = config, + domain = domain, + solution = solution, + reconstructor = reconstructor + # 注意:不预先占位 nothing! + ) + + # 2. 创建 boundary_condition(只依赖 config + domain) + boundary_condition = BoundaryConditionFactory.create(full_cfd) + full_cfd = merge(full_cfd, (boundary_condition = boundary_condition,)) + + # 3. 创建 residual_calculator(依赖上面所有字段) + residual_calculator = ResidualCalculator(full_cfd) + full_cfd = merge(full_cfd, (residual_calculator = residual_calculator,)) + + # 4. 创建 integrator(依赖 residual_calculator 等) + integrator = TimeIntegratorFactory.create(full_cfd) + full_cfd = merge(full_cfd, (integrator = integrator,)) + + # 5. 注入完整 self 到组件(确保它们能访问彼此) + residual_calculator.cfd = full_cfd + integrator.base.cfd = full_cfd + + result = Dict{String, Any}() + new(config, domain, solution, reconstructor, residual_calculator, integrator, boundary_condition, result) + end +end + +""" +通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界 +""" +function exact_solution(cfd::Cfd) + x = cfd.domain.mesh.xcc + T = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = @. (x - c * T + L) % L + + # ✅ 使用工厂创建初始条件(对标 Python) + ic = InitialConditionFactory.create(cfd.config.ic_type, cfd.config) + + return evaluate_at(ic, x_shifted) +end + +""" +主求解循环 +""" +function run!(cfd::Cfd) + # 应用初始边界条件并同步 old field + apply!(cfd.boundary_condition, cfd.solution.u) + update_old_field(cfd.solution) + + t = 0.0 + dt_old = cfd.config.dt + dt = dt_old + + while t < cfd.config.final_time + if t + dt > cfd.config.final_time + dt = cfd.config.final_time - t + end + #@show t, dt, maximum(cfd.solution.u), minimum(cfd.solution.u) + # 执行时间步 + step(cfd.integrator, dt) + t += dt + end + + # 恢复 dt + cfd.config.dt = dt_old + + # 整理结果 + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied-1] # Python: [ist:ied] + analytical = exact_solution(cfd) + + cfd.result = Dict( + "x" => cfd.domain.mesh.xcc, + "numerical" => u_numerical, + "analytical" => analytical, + "config" => Dict( + "scheme" => cfd.config.recon_scheme, + "order" => cfd.config.spatial_order, + "rk_order" => cfd.config.rk_order, + "final_time" => cfd.config.final_time + ) + ) + + return u_numerical +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03c/src/time_integration/base.jl b/example/1d-linear-convection/weno3/julia/03c/src/time_integration/base.jl new file mode 100644 index 00000000..a5b93bc1 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/time_integration/base.jl @@ -0,0 +1,33 @@ +# src/time_integration/base.jl + +""" +公共基类逻辑(对应 Python TimeIntegratorBase) +""" + +mutable struct TimeIntegratorBase + cfd::Any + config::Any + domain::Domain + solution::Solution + residual_calculator::Any +end + +function TimeIntegratorBase(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + residual_calculator = cfd.residual_calculator + TimeIntegratorBase(cfd, config, domain, solution, residual_calculator) +end + +function compute_residual(integrator::TimeIntegratorBase) + compute!(integrator.residual_calculator) +end + +function apply_boundary(integrator::TimeIntegratorBase) + apply!(integrator.cfd.boundary_condition, integrator.solution.u) +end + +function map_idx(integrator::TimeIntegratorBase, i::Int) + return i - integrator.domain.ist + 1 +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03c/src/time_integration/factory.jl b/example/1d-linear-convection/weno3/julia/03c/src/time_integration/factory.jl new file mode 100644 index 00000000..271fcde5 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/time_integration/factory.jl @@ -0,0 +1,29 @@ +# src/time_integration/factory.jl + +module TimeIntegratorFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "时间积分器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + rk_order = cfd.config.rk_order + name = "rk$rk_order" + if !haskey(_REGISTRY, name) + available = sort(collect(keys(_REGISTRY))) + error("未注册的时间积分器: '$name'。可用选项: $available") + end + return _REGISTRY[name](cfd) +end + +# ✅ 关键:用 Main. 引用在 Main 模块中定义的类型 +register("rk1", cfd -> Main.RK1Integrator(cfd)) +register("rk2", cfd -> Main.RK2Integrator(cfd)) +register("rk3", cfd -> Main.RK3Integrator(cfd)) + +end # module TimeIntegratorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03c/src/time_integration/rk1.jl b/example/1d-linear-convection/weno3/julia/03c/src/time_integration/rk1.jl new file mode 100644 index 00000000..580db359 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/time_integration/rk1.jl @@ -0,0 +1,19 @@ +# src/time_integration/rk1.jl + +mutable struct RK1Integrator + base::TimeIntegratorBase +end + +function RK1Integrator(cfd::Any) + RK1Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK1Integrator, dt::Float64) + compute_residual(integrator.base) + for i in integrator.base.domain.ist:(integrator.base.domain.ied - 1) + j = map_idx(integrator.base, i) + integrator.base.solution.u[i] += dt * integrator.base.solution.res[j] + end + apply_boundary(integrator.base) + update_old_field(integrator.base.solution) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03c/src/time_integration/rk2.jl b/example/1d-linear-convection/weno3/julia/03c/src/time_integration/rk2.jl new file mode 100644 index 00000000..ca2b7ab6 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/time_integration/rk2.jl @@ -0,0 +1,30 @@ +# src/time_integration/rk2.jl + +mutable struct RK2Integrator + base::TimeIntegratorBase +end + +function RK2Integrator(cfd::Any) + RK2Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK2Integrator, dt::Float64) + base = integrator.base + compute_residual(base) + u_pred = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u_pred[i] += dt * base.solution.res[j] + end + base.solution.u .= u_pred + apply_boundary(base) + compute_residual(base) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = 0.5 * base.solution.un[i] + + 0.5 * base.solution.u[i] + + 0.5 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03c/src/time_integration/rk3.jl b/example/1d-linear-convection/weno3/julia/03c/src/time_integration/rk3.jl new file mode 100644 index 00000000..a8e07736 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/time_integration/rk3.jl @@ -0,0 +1,44 @@ +# src/time_integration/rk3.jl + +mutable struct RK3Integrator + base::TimeIntegratorBase +end + +function RK3Integrator(cfd::Any) + RK3Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK3Integrator, dt::Float64) + base = integrator.base + # Stage 1 + compute_residual(base) + u1 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u1[i] += dt * base.solution.res[j] + end + base.solution.u .= u1 + apply_boundary(base) + # Stage 2 + compute_residual(base) + u2 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u2[i] = 0.75 * base.solution.un[i] + + 0.25 * base.solution.u[i] + + 0.25 * dt * base.solution.res[j] + end + base.solution.u .= u2 + apply_boundary(base) + # Stage 3 + compute_residual(base) + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = c1 * base.solution.un[i] + + c2 * base.solution.u[i] + + c3 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03c/src/time_integration/time_integration.jl b/example/1d-linear-convection/weno3/julia/03c/src/time_integration/time_integration.jl new file mode 100644 index 00000000..4e60bfc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/time_integration/time_integration.jl @@ -0,0 +1,7 @@ +# src/time_integration/time_integration.jl + +include("base.jl") +include("rk1.jl") +include("rk2.jl") +include("rk3.jl") +include("factory.jl") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03c/src/utils.jl b/example/1d-linear-convection/weno3/julia/03c/src/utils.jl new file mode 100644 index 00000000..3786cd1a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03c/src/utils.jl @@ -0,0 +1,9 @@ +# src/utils.jl + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end diff --git a/example/1d-linear-convection/weno3/julia/03d/examples/run_eno_weno.jl b/example/1d-linear-convection/weno3/julia/03d/examples/run_eno_weno.jl new file mode 100644 index 00000000..da24e6cd --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/examples/run_eno_weno.jl @@ -0,0 +1,50 @@ +# examples/run_eno_weno.jl +""" +1:1 复刻 run_eno_weno.py 的 Julia 版本 +""" + +include("../src/config.jl") +include("../src/mesh.jl") +include("../src/solver.jl") +include("../src/plotter.jl") + +function performEnoWenoAnalysis() + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + println("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + with_reconstruction(config_eno3, "eno", 3) # 显式指定 3 阶 + config_eno3.dt = 0.0025 # 覆盖默认值 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + run!(cfd_eno3) # 求解并生成 result 字典 + + # 3. 配置并运行 WENO3 求解 + println("Running WENO3 solver...") + config_weno3 = CfdConfig() + with_reconstruction(config_weno3, "weno", 3) # 显式指定 3 阶(WENO 默认 5 阶) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + run!(cfd_weno3) + + # 5. 绘制 ENO/WENO 对比图 + println("Plotting comparison results...") + plot_eno_weno_comparison( + cfd_eno3.result, + cfd_weno3.result; + save_path="eno_weno_comparison.png" + ) + + return cfd_eno3, cfd_weno3 +end + +# 主程序入口 +if abspath(PROGRAM_FILE) == @__FILE__ + performEnoWenoAnalysis() + println("Analysis completed!") +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/boundary.jl b/example/1d-linear-convection/weno3/julia/03d/src/boundary.jl new file mode 100644 index 00000000..82f58b71 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/boundary.jl @@ -0,0 +1,121 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +#using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end + + +# ---------------------- BoundaryConditionFactory ---------------------- +module BoundaryConditionFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "边界条件 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + bc_type = cfd.config.boundary_type + if !(bc_type isa AbstractString) + error("boundary_type 必须为字符串,当前值: $bc_type") + end + + if !haskey(_REGISTRY, bc_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的边界条件: '$bc_type'。可用选项: $available") + end + + return _REGISTRY[bc_type](cfd) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("periodic", cfd -> Main.PeriodicBoundary(cfd)) +register("dirichlet", cfd -> Main.DirichletBoundary(cfd)) +register("neumann", cfd -> Main.NeumannBoundary(cfd)) + +end # module BoundaryConditionFactory + +export BoundaryConditionFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/config.jl b/example/1d-linear-convection/weno3/julia/03d/src/config.jl new file mode 100644 index 00000000..1507d6a0 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/config.jl @@ -0,0 +1,73 @@ +# julia/config.jl +""" +CfdConfig:与 Python config.py 完全同构 +""" +mutable struct CfdConfig + ic_type::String + recon_scheme::String + flux_type::String + rk_order::Int + wave_speed::Float64 + final_time::Float64 + dt::Float64 + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + spatial_order::Int + equation_type::String # ← 新增 + problem_type::String # ← 新增 + + function CfdConfig() + new( + "step", + "eno", + "rusanov", + 1, + 1.0, + 0.625, + 0.025, + "periodic", + 1.0, + 2.0, + 2, + "linear_advection", # equation_type + "linear_advection" # problem_type + ) + end +end + +""" +专用配置:重建方案(链式调用) +""" +function with_reconstruction(cfg::CfdConfig, scheme::String, order::Union{Int, Nothing}=nothing) + cfg.recon_scheme = lowercase(scheme) + + if order !== nothing + cfg.spatial_order = order + else + if startswith(cfg.recon_scheme, "weno") + cfg.spatial_order = 5 + elseif cfg.recon_scheme == "eno" + cfg.spatial_order = 3 + else + error("不支持的重建格式:$scheme(仅支持 eno/weno)") + end + end + + return cfg # 支持链式调用 +end + +""" +专用配置:边界条件(链式调用) +""" +function with_boundary(cfg::CfdConfig, bc_type::String; left_value=nothing, right_value=nothing) + cfg.boundary_type = bc_type + if left_value !== nothing + cfg.left_boundary_value = left_value + end + if right_value !== nothing + cfg.right_boundary_value = right_value + end + return cfg +end + diff --git a/example/1d-linear-convection/weno3/julia/03d/src/domain.jl b/example/1d-linear-convection/weno3/julia/03d/src/domain.jl new file mode 100644 index 00000000..eec26114 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/domain.jl @@ -0,0 +1,58 @@ +# julia/domain.jl +""" +Domain 结构体(与 domain.py 完全同构) +- 保存 config +- nghosts 计算逻辑 1:1 +- ist/ied 为 0-based(与 Python 一致) +""" + +include("mesh.jl") +include("utils.jl") + +mutable struct Domain + config::Any # 保存 config(与 Python 一致) + mesh::Mesh + nghosts::Int + ist::Int # 0-based + ied::Int # 0-based + ntcells::Int +end + +function Domain(config::Any, mesh::Mesh) + nghosts = _calc_nghosts(config) + ist = nghosts + 1 # ← 1-based 起始索引 + ied = ist + mesh.ncells # ← 1-based 结束索引(不包含) + ntcells = mesh.ncells + 2 * nghosts + Domain(config, mesh, nghosts, ist, ied, ntcells) +end + +function _calc_nghosts(config::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + order = getfield_safe(config, :spatial_order, 2) + + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + if scheme == "eno" + nghosts = order + elseif startswith(scheme, "weno") + nghosts = order ÷ 2 + 1 + else + error("未知重建格式 $(scheme),无法计算ghost层!") + end + + if nghosts <= 0 + error("计算得到的ghost层数量无效:$(nghosts)(阶数$(order),格式$(scheme))") + end + + return nghosts +end + +function is_physical_cell(domain::Domain, idx::Int) + return domain.ist <= idx < domain.ied +end + +function get_physical_indices(domain::Domain) + return domain.ist:(domain.ied - 1) +end diff --git a/example/1d-linear-convection/weno3/julia/03d/src/equations/base.jl b/example/1d-linear-convection/weno3/julia/03d/src/equations/base.jl new file mode 100644 index 00000000..1915d7e3 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/equations/base.jl @@ -0,0 +1,28 @@ +# src/equations/base.jl + +""" +抽象方程基类 +所有具体方程必须实现: +- eq_flux(eq, u) +- max_wave_speed(eq, u) +""" +abstract type Equation end + +""" +通量函数 F(u) +""" +function eq_flux(eq::Equation, u::Float64)::Float64 + error("Not implemented for $(typeof(eq))") +end + +""" +最大波速(用于 Rusanov 通量和 CFL) +""" +function max_wave_speed(eq::Equation, u::Float64)::Float64 + error("Not implemented for $(typeof(eq))") +end + +""" +方程变量数(标量方程返回 1) +""" +num_equations(eq::Equation) = 1 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/equations/equations.jl b/example/1d-linear-convection/weno3/julia/03d/src/equations/equations.jl new file mode 100644 index 00000000..de1195d4 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/equations/equations.jl @@ -0,0 +1,4 @@ +# src/equations/equations.jl + +include("base.jl") +include("linear_advection.jl") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/equations/linear_advection.jl b/example/1d-linear-convection/weno3/julia/03d/src/equations/linear_advection.jl new file mode 100644 index 00000000..3086c1ce --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/equations/linear_advection.jl @@ -0,0 +1,16 @@ +# src/equations/linear_advection.jl + +""" +线性对流方程: ∂u/∂t + c ∂u/∂x = 0 +""" +mutable struct LinearAdvectionEquation <: Equation + wave_speed::Float64 + function LinearAdvectionEquation(config::Any) + c = getfield_safe(config, :wave_speed, 1.0) + new(c) + end +end + +eq_flux(eq::LinearAdvectionEquation, u::Float64) = eq.wave_speed * u + +max_wave_speed(eq::LinearAdvectionEquation, u::Float64) = abs(eq.wave_speed) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/flux/base.jl b/example/1d-linear-convection/weno3/julia/03d/src/flux/base.jl new file mode 100644 index 00000000..58dd6cc9 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/flux/base.jl @@ -0,0 +1,7 @@ +# src/flux/base.jl +""" +抽象通量计算器接口 +Julia 无 ABC,用文档约定: +- 所有子类型必须实现 `compute!(calc, qL, qR, flux)` +""" +abstract type InviscidFluxCalculator end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/flux/engquist_osher.jl b/example/1d-linear-convection/weno3/julia/03d/src/flux/engquist_osher.jl new file mode 100644 index 00000000..c0de183d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/flux/engquist_osher.jl @@ -0,0 +1,25 @@ +# src/flux/engquist_osher.jl +mutable struct EngquistOsherFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function EngquistOsherFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::EngquistOsherFluxCalculator, qL::Vector{Float64}, qR::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + c = calc.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = qL[i] + u_R = qR[i] + flux[i] = cp * u_L + cm * u_R + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/flux/factory.jl b/example/1d-linear-convection/weno3/julia/03d/src/flux/factory.jl new file mode 100644 index 00000000..6ae4917d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/flux/factory.jl @@ -0,0 +1,26 @@ +# src/flux/factory.jl + +module FluxCalculatorFactory + +# 全局注册表 +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "通量计算器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + flux_type = cfd.config.flux_type + if !haskey(_REGISTRY, flux_type) + error("未注册的通量计算器: '$flux_type'。可用: $(keys(_REGISTRY))") + end + return _REGISTRY[flux_type](cfd) +end + +register("rusanov", cfd -> Main.RusanovFluxCalculator(cfd)) +register("engquist-osher", cfd -> Main.EngquistOsherFluxCalculator(cfd)) + +end # module FluxCalculatorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/flux/flux.jl b/example/1d-linear-convection/weno3/julia/03d/src/flux/flux.jl new file mode 100644 index 00000000..533f034c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/flux/flux.jl @@ -0,0 +1,10 @@ +# src/flux/flux.jl + +# 加载组件(顺序很重要!) +include("base.jl") +include("rusanov.jl") +include("engquist_osher.jl") +include("factory.jl") + +# 导出(如果你未来用模块) +# export InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/flux/rusanov.jl b/example/1d-linear-convection/weno3/julia/03d/src/flux/rusanov.jl new file mode 100644 index 00000000..70c20fc3 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/flux/rusanov.jl @@ -0,0 +1,28 @@ +# src/flux/rusanov.jl + +mutable struct RusanovFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function RusanovFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::RusanovFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + for i in 1:calc.mesh.nnodes + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = calc.wave_speed + c_R = calc.wave_speed + F_L = c_L * u_L + F_R = c_R * u_R + Smax = max(abs(c_L), abs(c_R)) + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/initial_condition.jl b/example/1d-linear-convection/weno3/julia/03d/src/initial_condition.jl new file mode 100644 index 00000000..1b9993aa --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/initial_condition.jl @@ -0,0 +1,112 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end + +# ---------------------- InitialConditionFactory ---------------------- +module InitialConditionFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "初始条件 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(ic_type::String, config::Any) + if !(ic_type isa AbstractString) + error("ic_type 必须为字符串,当前值: $ic_type") + end + + if !haskey(_REGISTRY, ic_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的初始条件: '$ic_type'。可用选项: $available") + end + + return _REGISTRY[ic_type](config) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("step", config -> Main.StepFunctionIC(config)) +register("sin", config -> Main.SineWaveIC(config)) +register("gaussian", config -> Main.GaussianPulseIC(config)) + +end # module InitialConditionFactory + +export InitialConditionFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/mesh.jl b/example/1d-linear-convection/weno3/julia/03d/src/mesh.jl new file mode 100644 index 00000000..1b0dd4bf --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/mesh.jl @@ -0,0 +1,45 @@ +# julia/mesh.jl +""" +Mesh 结构体(与提供的 mesh.py 完全同构) +- 字段名、初始化顺序、循环逻辑完全一致 +- 不做任何泛化或优化 +""" + +mutable struct Mesh + xmin::Float64 + xmax::Float64 + ncells::Int + nnodes::Int + nx::Int + x::Vector{Float64} + xcc::Vector{Float64} + L::Float64 # 在 init_mesh 中赋值 + dx::Float64 # 在 init_mesh 中赋值 + + function Mesh() + # 与 Python __init__ 完全一致 + xmin = 0.0 + xmax = 2.0 + ncells = 40 + nnodes = ncells + 1 + nx = ncells + x = zeros(Float64, nnodes) + xcc = zeros(Float64, ncells) + + # 调用 init_mesh(模拟 Python) + L = xmax - xmin + dx = L / ncells + + # Generate node coordinates: for i in range(self.nnodes) + for i in 1:nnodes + x[i] = xmin + (i - 1) * dx # Python i → Julia i-1 + end + + # Generate cell center coordinates + for i in 1:ncells + xcc[i] = 0.5 * (x[i] + x[i+1]) + end + + new(xmin, xmax, ncells, nnodes, nx, x, xcc, L, dx) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/plotter.jl b/example/1d-linear-convection/weno3/julia/03d/src/plotter.jl new file mode 100644 index 00000000..a77d8d68 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/plotter.jl @@ -0,0 +1,147 @@ +# julia/plotter.jl +""" +CFDPlotter 的 Julia 实现(通过 PythonCall.jl 调用 Matplotlib) +确保与 Python plotter.py 行为完全一致 +""" + +using PythonCall + +# 初始化 Python 环境(加载 matplotlib, inflect) +const plt = pyimport("matplotlib.pyplot") +const inflect = pyimport("inflect") + +mutable struct CFDPlotter + default_styles::Dict{String, Any} + p::Py +end + +function CFDPlotter() + default_styles = Dict{String, Any}( + "numerical" => Dict( + :color => "blue", + :linestyle => "-", + :marker => "o", + :markerfacecolor => "none" + ), + "analytical" => Dict( + :color => "red", + :linestyle => "--", + :marker => "", + :linewidth => 1.5 + ), + "comparison" => [ + Dict(:color => "black", :linestyle => "-", :marker => "o", :markerfacecolor => "none"), + Dict(:color => "blue", :linestyle => "--", :marker => "s", :markerfacecolor => "none"), + Dict(:color => "green", :linestyle => ":", :marker => "^", :markerfacecolor => "none") + ] + ) + p = inflect.engine() + CFDPlotter(default_styles, p) +end + +""" +轻量即时绘图(快速验证结果) +""" +function plot_quick(plotter::CFDPlotter, cfd_result::Dict; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + rk_order = cfd_result["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = cfd_result["config"]["final_time"] + order = cfd_result["config"]["order"] + scheme = uppercase(cfd_result["config"]["scheme"]) + actual_title = "1D Convection (t=$(final_time))\n$(order)th-order $(scheme) + $(rk_str)-order RK" + end + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"]; + label="Numerical ($(uppercase(cfd_result["config"]["scheme"])))", + plotter.default_styles["numerical"]..., + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + # 通用样式 + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +""" +多格式/多精度对比绘图 +""" +function plot_comparison(plotter::CFDPlotter, result_list::Vector{Dict{String, Any}}; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + schemes = [uppercase(r["config"]["scheme"]) * string(r["config"]["order"]) for r in result_list] + rk_order = result_list[1]["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = result_list[1]["config"]["final_time"] + actual_title = "1D Convection Comparison (t=$(final_time))\n$(join(schemes, ", ")) + $(rk_str)-order RK" + end + + # 绘制多个数值解 + for (i, res) in enumerate(result_list) + style = plotter.default_styles["comparison"][mod1(i, length(plotter.default_styles["comparison"]))] + label = "Numerical ($(uppercase(res["config"]["scheme"]))$(res["config"]["order"]))" + plt.plot( + res["x"], res["numerical"]; + label=label, + style..., + markersize=5, linewidth=0.5 + ) + end + + # 绘制解析解 + plt.plot( + result_list[1]["x"], result_list[1]["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +function _set_common_style(plotter::CFDPlotter, title::String) + plt.title(title, fontsize=12) + plt.xlabel("x", fontsize=10) + plt.ylabel("u", fontsize=10) + plt.legend(fontsize=9) + plt.grid(true, color="gray", linestyle="--", linewidth=0.5, alpha=0.7) + plt.tight_layout() +end + +""" +快捷函数:ENO/WENO对比绘图 +""" +function plot_eno_weno_comparison(eno_result::Dict, weno_result::Dict; save_path=nothing) + plotter = CFDPlotter() + plot_comparison(plotter, [eno_result, weno_result]; save_path=save_path) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/problems/base.jl b/example/1d-linear-convection/weno3/julia/03d/src/problems/base.jl new file mode 100644 index 00000000..e29d0fbb --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/problems/base.jl @@ -0,0 +1,22 @@ +# src/problems/base.jl + +""" +抽象问题基类 +所有具体问题必须实现: +- create_initial_condition(prob, config) +- create_boundary_condition(prob, cfd) +- exact_solution(prob, x, t) +""" +abstract type Problem end + +function create_initial_condition(prob::Problem, config::Any) + error("Not implemented for $(typeof(prob))") +end + +function create_boundary_condition(prob::Problem, cfd::Any) + error("Not implemented for $(typeof(prob))") +end + +function exact_solution(prob::Problem, x::Vector{Float64}, t::Float64)::Vector{Float64} + error("Not implemented for $(typeof(prob))") +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/problems/linear_advection.jl b/example/1d-linear-convection/weno3/julia/03d/src/problems/linear_advection.jl new file mode 100644 index 00000000..8b37d9f8 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/problems/linear_advection.jl @@ -0,0 +1,43 @@ +# src/problems/linear_advection.jl + +""" +线性对流问题:定义初始条件、边界条件、解析解 +""" +mutable struct LinearAdvectionProblem <: Problem + ic_type::String + boundary_type::String + left_value::Float64 + right_value::Float64 + domain_length::Float64 + wave_speed::Float64 # 从 config 获取,未来可从 equation 获取 + + function LinearAdvectionProblem(config::Any) + new( + getfield_safe(config, :ic_type, "step"), + getfield_safe(config, :boundary_type, "periodic"), + getfield_safe(config, :left_boundary_value, 1.0), + getfield_safe(config, :right_boundary_value, 2.0), + getfield_safe(config, :domain_length, 2.0), + getfield_safe(config, :wave_speed, 1.0) + ) + end +end + +function create_initial_condition(prob::LinearAdvectionProblem, config::Any) + return Main.InitialConditionFactory.create(prob.ic_type, config) +end + +function create_boundary_condition(prob::LinearAdvectionProblem, cfd::Any) + # 复用原有 BC 工厂(cfd 需包含 config + domain) + return Main.BoundaryConditionFactory.create(cfd) +end + +function exact_solution(prob::LinearAdvectionProblem, x::Vector{Float64}, t::Float64)::Vector{Float64} + L = prob.domain_length + c = prob.wave_speed + # 周期性平移 + x_shifted = @. (x - c * t + L) % L + # 重用 IC 的 evaluate_at + ic = Main.InitialConditionFactory.create(prob.ic_type, (ic_type=prob.ic_type,)) + return Main.evaluate_at(ic, x_shifted) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/problems/problems.jl b/example/1d-linear-convection/weno3/julia/03d/src/problems/problems.jl new file mode 100644 index 00000000..98311397 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/problems/problems.jl @@ -0,0 +1,4 @@ +# src/problems/problems.jl + +include("base.jl") +include("linear_advection.jl") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/reconstructor/eno.jl b/example/1d-linear-convection/weno3/julia/03d/src/reconstructor/eno.jl new file mode 100644 index 00000000..e78a636f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/reconstructor/eno.jl @@ -0,0 +1,107 @@ +# julia/reconstructor/eno.jl +""" +ENO 重构器(与 reconstructor/eno.py 完全同构) +""" + +# ---------------------- ENO 系数初始化 ---------------------- +function _init_eno_coef!(spatial_order::Int, coef::Matrix{Float64}) + if spatial_order == 1 + coef[1, 1] = 1.0 + coef[2, 1] = 1.0 + elseif spatial_order == 2 + coef[1, 1:2] = [3.0/2.0, -1.0/2.0] + coef[2, 1:2] = [1.0/2.0, 1.0/2.0] + coef[3, 1:2] = [-1.0/2.0, 3.0/2.0] + elseif spatial_order == 3 + coef[1, 1:3] = [11.0/6.0, -7.0/6.0, 1.0/3.0] + coef[2, 1:3] = [1.0/3.0, 5.0/6.0, -1.0/6.0] + coef[3, 1:3] = [-1.0/6.0, 5.0/6.0, 1.0/3.0] + coef[4, 1:3] = [1.0/3.0, -7.0/6.0, 11.0/6.0] + elseif spatial_order == 4 + coef[1, 1:4] = [25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0] + coef[2, 1:4] = [1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0] + coef[3, 1:4] = [-1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0] + coef[4, 1:4] = [1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0] + coef[5, 1:4] = [-1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0] + elseif spatial_order == 5 + coef[1, 1:5] = [137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0] + coef[2, 1:5] = [1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0] + coef[3, 1:5] = [-1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0] + coef[4, 1:5] = [1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0] + coef[5, 1:5] = [-1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0] + coef[6, 1:5] = [1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0] + elseif spatial_order == 6 + coef[1, 1:6] = [49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0] + coef[2, 1:6] = [1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0] + coef[3, 1:6] = [-1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0] + coef[4, 1:6] = [1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0] + coef[5, 1:6] = [-1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0] + coef[6, 1:6] = [1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0] + coef[7, 1:6] = [-1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0] + elseif spatial_order == 7 + coef[1, 1:7] = [363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0] + coef[2, 1:7] = [1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0] + coef[3, 1:7] = [-1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0] + coef[4, 1:7] = [1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0] + coef[5, 1:7] = [-1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0] + coef[6, 1:7] = [1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0] + coef[7, 1:7] = [-1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0] + coef[8, 1:7] = [1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0] + else + error("ENO 系数未实现 order=$spatial_order") + end +end + +# ---------------------- ENO 重构器 ---------------------- +mutable struct EnoReconstructor + spatial_order::Int + ntcells::Int + lmc::Vector{Int} + coef::Matrix{Float64} + dd::Matrix{Float64} + + function EnoReconstructor(spatial_order::Int, ntcells::Int) + lmc = zeros(Int, ntcells) + coef = zeros(Float64, spatial_order + 1, spatial_order) + dd = zeros(Float64, spatial_order, ntcells) + _init_eno_coef!(spatial_order, coef) + new(spatial_order, ntcells, lmc, coef, dd) + end +end + +function reconstruct(rec::EnoReconstructor, q::Vector{Float64}, cfd::Any) + # 1. 差商计算 (dd[1,:] = q) + @views rec.dd[1, :] .= q + for m in 2:rec.spatial_order + for j in 1:(rec.ntcells - m + 1) + rec.dd[m, j] = rec.dd[m-1, j+1] - rec.dd[m-1, j] + end + end + + # 2. 选择 smoothest stencil + domain = cfd.domain + for i in (domain.ist - 1):(domain.ied) # Python: range(ist-1, ied+1) → ied+1-1 = ied + rec.lmc[i] = i + for m in 2:rec.spatial_order + if abs(rec.dd[m, rec.lmc[i] - 1]) < abs(rec.dd[m, rec.lmc[i]]) + rec.lmc[i] -= 1 + end + end + end + + # 3. 重构界面值 + solution = cfd.solution + for i in domain.ist:(domain.ied) # Python: range(ist, ied+1) → ied+1-1 = ied + j = i - domain.ist + 1 # Julia 1-based + k1 = rec.lmc[i - 1] + k2 = rec.lmc[i] + r1 = (i - 1) - k1 + 1 + r2 = i - k2 + 1 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in 1:rec.spatial_order + solution.q_face_left[j] += q[k1 + m - 1] * rec.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m - 1] * rec.coef[r2, m] + end + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/reconstructor/factory.jl b/example/1d-linear-convection/weno3/julia/03d/src/reconstructor/factory.jl new file mode 100644 index 00000000..70b0cf9f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/reconstructor/factory.jl @@ -0,0 +1,38 @@ +# src/reconstructor/factory.jl + +""" +ReconstructorFactory +对标 Python: ReconstructorFactory.create(config, domain) +""" +module ReconstructorFactory + +include("../utils.jl") + +# ✅ 不要用 using ..EnoReconstructor(它们不是模块) +# 直接通过 Main. 引用顶层定义的类型 + +function create(config::Any, domain::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + # 处理 WENO 默认命名(如 Python) + if scheme == "weno" + order = getfield_safe(config, :spatial_order, 5) + scheme = "weno$(order)" + end + + if scheme == "eno" + order = getfield_safe(config, :spatial_order, 3) + return Main.EnoReconstructor(order, domain.ntcells) + elseif scheme == "weno3" + return Main.Weno3Reconstructor() + else + error("不支持的重建格式: $scheme(仅支持 eno/weno3)") + end +end + +end # module ReconstructorFactory + +export ReconstructorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/reconstructor/weno3.jl b/example/1d-linear-convection/weno3/julia/03d/src/reconstructor/weno3.jl new file mode 100644 index 00000000..2b6fe1ab --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/reconstructor/weno3.jl @@ -0,0 +1,65 @@ +# julia/reconstructor/weno3.jl +""" +WENO3 重构器(与 reconstructor/weno3.py 完全同构) +""" + +mutable struct Weno3Reconstructor + # 无字段,与 Python 一致 +end + +function reconstruct(rec::Weno3Reconstructor, q::Vector{Float64}, cfd::Any) + domain = cfd.domain + solution = cfd.solution + _reconstruct_left_interfaces(domain, q, solution.q_face_left) + _reconstruct_right_interfaces(domain, q, solution.q_face_right) +end + +function _reconstruct_left_interfaces(domain, u, qL) + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in (domain.ist - 1):(domain.ied - 1) + j = i - (domain.ist - 1) + 1 # ← Julia 1-based: j = i - (ist-1) 对应 Python j = i - (ist-1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = _reconstruct_from_left_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_right_interfaces(domain, u, qR) + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in domain.ist:domain.ied + j = i - domain.ist + 1 # ← Julia 1-based: j = i - ist 对应 Python j = i - ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = _reconstruct_from_right_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_from_left_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -0.5*v1 + 1.5*v2 # r=1 stencil + q1 = 0.5*v2 + 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end + +function _reconstruct_from_right_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 0.5*v1 + 0.5*v2 # r=1 stencil + q1 = 1.5*v2 - 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/registry.jl b/example/1d-linear-convection/weno3/julia/03d/src/registry.jl new file mode 100644 index 00000000..6acd9aaa --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/registry.jl @@ -0,0 +1,164 @@ +""" +统一注册系统 + 通用工厂 +- ComponentRegistry: 组件注册表 +- @register_component: 装饰器宏 +- BaseFactory: 通用工厂接口 +""" + +module ComponentRegistry + +# 注册表:Dict{category => Dict{name => constructor}} +const _REGISTRIES = Dict{String, Dict{String, Function}}() +const _VERBOSE = Ref(true) + +# ---------------------- 注册核心逻辑 ---------------------- +""" + register(category::String, name::String, ctor::Function) + +注册一个组件构造函数到指定类别。 +如果已存在同名组件且不同,则发出警告。 +""" +function register(category::String, name::String, ctor::Function) + if !haskey(_REGISTRIES, category) + _REGISTRIES[category] = Dict{String, Function}() + end + + registry = _REGISTRIES[category] + if haskey(registry, name) + if registry[name] !== ctor && _VERBOSE[] + @warn "覆盖注册: $category.$name" + end + end + + registry[name] = ctor + if _VERBOSE[] + println("✅ 已注册: $category.$name -> $(nameof(ctor))") + end +end + +""" + create(category::String, name::String, args...; kwargs...) + +从注册表创建组件实例。 +""" +function create(category::String, name::String, args...; kwargs...) + if !haskey(_REGISTRIES, category) + error("❌ 未知类别: $category (可用: $(collect(keys(_REGISTRIES))))") + end + + registry = _REGISTRIES[category] + lname = lowercase(name) + if !haskey(registry, lname) + available = sort(collect(keys(registry))) + error("❌ 未找到: $category.$name (可用: $available)") + end + + return registry[lname](args...; kwargs...) +end + +""" + list_all() + +返回所有已注册组件(按类别)。 +""" +function list_all() + return Dict(cat => sort(collect(keys(reg))) for (cat, reg) in _REGISTRIES) +end + +""" + set_verbose(flag::Bool) + +开启/关闭注册提示。 +""" +function set_verbose(flag::Bool) + _VERBOSE[] = flag +end + +end # module ComponentRegistry + + +# ---------------------- 装饰器宏:@register_component ---------------------- +""" +@register_component(category, [name]) + +用法: + @register_component("boundary", "periodic") + struct PeriodicBoundary ... + +若省略 name,则使用类型名的小写形式。 +""" +macro register_component(category::String, name_expr) + error("@register_component 需在类型定义前使用,且必须在模块顶层") +end + +macro register_component(category::String) + error("@register_component(category, name) 需指定 name 或在类型后使用") +end + +# 重载:@register_component("category", "name") struct X ... end +macro register_component(category::String, name::String, ex) + if !Meta.isexpr(ex, :struct) + error("@register_component 必须作用于 struct 定义") + end + + struct_name = ex.args[2] + if Meta.isexpr(struct_name, :curly) + struct_name = struct_name.args[1] + end + + # 插入注册调用(在模块顶层) + quote + $(esc(ex)) + $(ComponentRegistry).register($(category), $(name), $(esc(struct_name))) + end +end + +# 重载:@register_component("category") struct X ... end → name = lowercase(nameof(X)) +macro register_component(category::String, ex) + if !Meta.isexpr(ex, :struct) + error("@register_component 必须作用于 struct 定义") + end + + struct_name = ex.args[2] + if Meta.isexpr(struct_name, :curly) + struct_name = struct_name.args[1] + end + + name_str = string(struct_name) |> lowercase + + quote + $(esc(ex)) + $(ComponentRegistry).register($(category), $(name_str), $(esc(struct_name))) + end +end + + +# ---------------------- 通用工厂 ---------------------- +module BaseFactory + +using ..ComponentRegistry + +""" + create_component(category::String, name::String, args...; kwargs...) + +通用工厂接口,与 Python BaseFactory.create_component 行为一致。 +""" +function create_component(category::String, name::String, args...; kwargs...) + return ComponentRegistry.create(category, name, args...; kwargs...) +end + +""" + get_available_components(category::String) + +列出某类别下所有可用组件。 +""" +function get_available_components(category::String) + all = ComponentRegistry.list_all() + return get(all, category, String[]) +end + +end # module BaseFactory + + +# 导出接口 +export ComponentRegistry, BaseFactory, @register_component \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/residual.jl b/example/1d-linear-convection/weno3/julia/03d/src/residual.jl new file mode 100644 index 00000000..11a88593 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/residual.jl @@ -0,0 +1,69 @@ +# julia/residual.jl +""" +残差计算器(与 residual.py 完全同构) +- 封装重建→通量→散度完整流程 +- 依赖 cfd 的多个字段 +""" + +include("mesh.jl") + +mutable struct ResidualCalculator + cfd::Any + config::Any + domain::Any + solution::Any + mesh::Mesh + reconstructor::Any + flux_calculator::Any + + function ResidualCalculator(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + mesh = domain.mesh + reconstructor = cfd.reconstructor + + # ✅ 内部创建 flux_calculator(对标 Python) + flux_calculator = FluxCalculatorFactory.create(cfd) + + new(cfd, config, domain, solution, mesh, reconstructor, flux_calculator) + end +end + + +""" +计算完整残差(对外唯一接口) +""" +function compute!(calc::ResidualCalculator) + _reconstruct(calc) + _compute_inviscid_flux(calc) + _compute_flux_divergence(calc) +end + +""" +私有方法:界面值重建 +""" +function _reconstruct(calc::ResidualCalculator) + reconstruct(calc.reconstructor, calc.solution.u, calc.cfd) +end + +""" +私有方法:计算无粘通量 +""" +function _compute_inviscid_flux(calc::ResidualCalculator) + compute!(calc.flux_calculator, + calc.solution.q_face_left, + calc.solution.q_face_right, + calc.solution.flux) +end + +""" +私有方法:计算通量散度(残差 = -dF/dx) +""" +function _compute_flux_divergence(calc::ResidualCalculator) + solution = calc.solution + mesh = calc.mesh + for i in 1:mesh.ncells + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / mesh.dx + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/solution.jl b/example/1d-linear-convection/weno3/julia/03d/src/solution.jl new file mode 100644 index 00000000..7044c001 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/solution.jl @@ -0,0 +1,60 @@ +# julia/solution.jl +""" +Solution 结构体(与真实 solution.py 完全同构) +字段顺序、方法、逻辑 1:1 对齐 +""" + +include("mesh.jl") +include("domain.jl") +include("initial_condition.jl") + +mutable struct Solution + domain::Domain + q_face_left::Vector{Float64} + q_face_right::Vector{Float64} + flux::Vector{Float64} + res::Vector{Float64} + u::Vector{Float64} + un::Vector{Float64} + + function Solution(config::Any, domain::Domain) + mesh = domain.mesh + + q_face_left = zeros(Float64, mesh.nnodes) + q_face_right = zeros(Float64, mesh.nnodes) + flux = zeros(Float64, mesh.nnodes) + res = zeros(Float64, mesh.ncells) + u = zeros(Float64, domain.ntcells) + un = zeros(Float64, domain.ntcells) + + sol = new(domain, q_face_left, q_face_right, flux, res, u, un) + initialize_from_config(sol, config) + return sol + end +end + +""" +重置解数组为初始状态 +""" +function reset_solution(sol::Solution) + fill!(sol.u, 0.0) + fill!(sol.un, 0.0) +end + +""" +根据配置初始化场 +""" + +function initialize_from_config(sol::Solution, config::Any) + ic_type = getfield_safe(config, :ic_type, "step") + ic = InitialConditionFactory.create(ic_type, config) + apply(ic, sol) +end + +""" +更新旧场:un = u +""" +function update_old_field(sol::Solution) + sol.un .= sol.u # 等价于 Python 的 un[:] = u[:] +end + diff --git a/example/1d-linear-convection/weno3/julia/03d/src/solver.jl b/example/1d-linear-convection/weno3/julia/03d/src/solver.jl new file mode 100644 index 00000000..b2e975aa --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/solver.jl @@ -0,0 +1,162 @@ +# julia/solver.jl +""" +CFD 求解器主类(与 solver.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("initial_condition.jl") +include("boundary.jl") +include("flux/flux.jl") +include("residual.jl") +include("time_integration/time_integration.jl") +include("reconstructor/eno.jl") +include("reconstructor/weno3.jl") +include("reconstructor/factory.jl") + +include("equations/equations.jl") +include("problems/problems.jl") + +# 导入工厂模块(必须在顶层!) +using .FluxCalculatorFactory +using .BoundaryConditionFactory +using .ReconstructorFactory + +# ---------------------- Cfd 求解器 ---------------------- +mutable struct Cfd + config::Any + domain::Domain + solution::Solution + reconstructor::Any + residual_calculator::ResidualCalculator + integrator::Any + boundary_condition::Any + result::Dict{String, Any} + + function Cfd(config::Any, mesh::Mesh) + domain = Domain(config, mesh) + solution = Solution(config, domain) + reconstructor = ReconstructorFactory.create(config, domain) + + # 1. 创建 Equation 和 Problem + equation = create_equation(config) + problem = create_problem(config) + + # 2. 初始上下文(仅包含不依赖其他组件的字段) + full_cfd = ( + config = config, + domain = domain, + solution = solution, + reconstructor = reconstructor, + equation = equation, # ← 新增 + problem = problem # ← 新增 + ) + + # 3. 创建边界条件(通过 problem) + boundary_condition = create_boundary_condition(problem, full_cfd) + full_cfd = merge(full_cfd, (boundary_condition = boundary_condition,)) + + + # 4. 创建 residual_calculator(现在可访问 equation) + residual_calculator = ResidualCalculator(full_cfd) + full_cfd = merge(full_cfd, (residual_calculator = residual_calculator,)) + + # 5. 创建 integrator + integrator = TimeIntegratorFactory.create(full_cfd) + full_cfd = merge(full_cfd, (integrator = integrator,)) + + # 6. 注入完整 self + residual_calculator.cfd = full_cfd + integrator.base.cfd = full_cfd + + # 7. 初始化解(通过 problem) + ic = create_initial_condition(problem, config) + Main.apply(ic, solution) # 注意:apply 在 Main + + result = Dict{String, Any}() + new(config, domain, solution, reconstructor, residual_calculator, integrator, boundary_condition, result) + + end +end + +""" +通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界 +""" +function exact_solution(cfd::Cfd) + x = cfd.domain.mesh.xcc + T = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = @. (x - c * T + L) % L + + # ✅ 使用工厂创建初始条件(对标 Python) + ic = InitialConditionFactory.create(cfd.config.ic_type, cfd.config) + + return evaluate_at(ic, x_shifted) +end + +""" +主求解循环 +""" +function run!(cfd::Cfd) + # 应用初始边界条件并同步 old field + apply!(cfd.boundary_condition, cfd.solution.u) + update_old_field(cfd.solution) + + t = 0.0 + dt_old = cfd.config.dt + dt = dt_old + + while t < cfd.config.final_time + if t + dt > cfd.config.final_time + dt = cfd.config.final_time - t + end + #@show t, dt, maximum(cfd.solution.u), minimum(cfd.solution.u) + # 执行时间步 + step(cfd.integrator, dt) + t += dt + end + + # 恢复 dt + cfd.config.dt = dt_old + + # 整理结果 + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied-1] # Python: [ist:ied] + analytical = exact_solution(cfd) + + cfd.result = Dict( + "x" => cfd.domain.mesh.xcc, + "numerical" => u_numerical, + "analytical" => analytical, + "config" => Dict( + "scheme" => cfd.config.recon_scheme, + "order" => cfd.config.spatial_order, + "rk_order" => cfd.config.rk_order, + "final_time" => cfd.config.final_time + ) + ) + + return u_numerical +end + +# --- Equation / Problem 创建函数 --- +function create_equation(config::Any) + eq_type = config.equation_type + if eq_type == "linear_advection" + return LinearAdvectionEquation(config) + else + error("Unknown equation type: $eq_type") + end +end + +function create_problem(config::Any) + prob_type = config.problem_type + if prob_type == "linear_advection" + return LinearAdvectionProblem(config) + else + error("Unknown problem type: $prob_type") + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/time_integration/base.jl b/example/1d-linear-convection/weno3/julia/03d/src/time_integration/base.jl new file mode 100644 index 00000000..a5b93bc1 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/time_integration/base.jl @@ -0,0 +1,33 @@ +# src/time_integration/base.jl + +""" +公共基类逻辑(对应 Python TimeIntegratorBase) +""" + +mutable struct TimeIntegratorBase + cfd::Any + config::Any + domain::Domain + solution::Solution + residual_calculator::Any +end + +function TimeIntegratorBase(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + residual_calculator = cfd.residual_calculator + TimeIntegratorBase(cfd, config, domain, solution, residual_calculator) +end + +function compute_residual(integrator::TimeIntegratorBase) + compute!(integrator.residual_calculator) +end + +function apply_boundary(integrator::TimeIntegratorBase) + apply!(integrator.cfd.boundary_condition, integrator.solution.u) +end + +function map_idx(integrator::TimeIntegratorBase, i::Int) + return i - integrator.domain.ist + 1 +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/time_integration/factory.jl b/example/1d-linear-convection/weno3/julia/03d/src/time_integration/factory.jl new file mode 100644 index 00000000..271fcde5 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/time_integration/factory.jl @@ -0,0 +1,29 @@ +# src/time_integration/factory.jl + +module TimeIntegratorFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "时间积分器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + rk_order = cfd.config.rk_order + name = "rk$rk_order" + if !haskey(_REGISTRY, name) + available = sort(collect(keys(_REGISTRY))) + error("未注册的时间积分器: '$name'。可用选项: $available") + end + return _REGISTRY[name](cfd) +end + +# ✅ 关键:用 Main. 引用在 Main 模块中定义的类型 +register("rk1", cfd -> Main.RK1Integrator(cfd)) +register("rk2", cfd -> Main.RK2Integrator(cfd)) +register("rk3", cfd -> Main.RK3Integrator(cfd)) + +end # module TimeIntegratorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/time_integration/rk1.jl b/example/1d-linear-convection/weno3/julia/03d/src/time_integration/rk1.jl new file mode 100644 index 00000000..580db359 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/time_integration/rk1.jl @@ -0,0 +1,19 @@ +# src/time_integration/rk1.jl + +mutable struct RK1Integrator + base::TimeIntegratorBase +end + +function RK1Integrator(cfd::Any) + RK1Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK1Integrator, dt::Float64) + compute_residual(integrator.base) + for i in integrator.base.domain.ist:(integrator.base.domain.ied - 1) + j = map_idx(integrator.base, i) + integrator.base.solution.u[i] += dt * integrator.base.solution.res[j] + end + apply_boundary(integrator.base) + update_old_field(integrator.base.solution) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/time_integration/rk2.jl b/example/1d-linear-convection/weno3/julia/03d/src/time_integration/rk2.jl new file mode 100644 index 00000000..ca2b7ab6 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/time_integration/rk2.jl @@ -0,0 +1,30 @@ +# src/time_integration/rk2.jl + +mutable struct RK2Integrator + base::TimeIntegratorBase +end + +function RK2Integrator(cfd::Any) + RK2Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK2Integrator, dt::Float64) + base = integrator.base + compute_residual(base) + u_pred = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u_pred[i] += dt * base.solution.res[j] + end + base.solution.u .= u_pred + apply_boundary(base) + compute_residual(base) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = 0.5 * base.solution.un[i] + + 0.5 * base.solution.u[i] + + 0.5 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/time_integration/rk3.jl b/example/1d-linear-convection/weno3/julia/03d/src/time_integration/rk3.jl new file mode 100644 index 00000000..a8e07736 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/time_integration/rk3.jl @@ -0,0 +1,44 @@ +# src/time_integration/rk3.jl + +mutable struct RK3Integrator + base::TimeIntegratorBase +end + +function RK3Integrator(cfd::Any) + RK3Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK3Integrator, dt::Float64) + base = integrator.base + # Stage 1 + compute_residual(base) + u1 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u1[i] += dt * base.solution.res[j] + end + base.solution.u .= u1 + apply_boundary(base) + # Stage 2 + compute_residual(base) + u2 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u2[i] = 0.75 * base.solution.un[i] + + 0.25 * base.solution.u[i] + + 0.25 * dt * base.solution.res[j] + end + base.solution.u .= u2 + apply_boundary(base) + # Stage 3 + compute_residual(base) + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = c1 * base.solution.un[i] + + c2 * base.solution.u[i] + + c3 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/time_integration/time_integration.jl b/example/1d-linear-convection/weno3/julia/03d/src/time_integration/time_integration.jl new file mode 100644 index 00000000..4e60bfc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/time_integration/time_integration.jl @@ -0,0 +1,7 @@ +# src/time_integration/time_integration.jl + +include("base.jl") +include("rk1.jl") +include("rk2.jl") +include("rk3.jl") +include("factory.jl") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03d/src/utils.jl b/example/1d-linear-convection/weno3/julia/03d/src/utils.jl new file mode 100644 index 00000000..3786cd1a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03d/src/utils.jl @@ -0,0 +1,9 @@ +# src/utils.jl + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end diff --git a/example/1d-linear-convection/weno3/julia/03f/examples/run_eno_weno.jl b/example/1d-linear-convection/weno3/julia/03f/examples/run_eno_weno.jl new file mode 100644 index 00000000..f02110d9 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/examples/run_eno_weno.jl @@ -0,0 +1,99 @@ +# examples/run_eno_weno.jl +""" +1:1 复刻 run_eno_weno.py 的 Julia 版本 +""" + +include("../src/config.jl") +include("../src/mesh.jl") +include("../src/solver.jl") +include("../src/plotter.jl") + +function performEnoWenoAnalysisBAK() + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + println("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + with_reconstruction(config_eno3, "eno", 3) # 显式指定 3 阶 + config_eno3.dt = 0.0025 # 覆盖默认值 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + run!(cfd_eno3) # 求解并生成 result 字典 + + # 3. 配置并运行 WENO3 求解 + println("Running WENO3 solver...") + config_weno3 = CfdConfig() + with_reconstruction(config_weno3, "weno", 3) # 显式指定 3 阶(WENO 默认 5 阶) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + run!(cfd_weno3) + + # 5. 绘制 ENO/WENO 对比图 + println("Plotting comparison results...") + plot_eno_weno_comparison( + cfd_eno3.result, + cfd_weno3.result; + save_path="eno_weno_comparison.png" + ) + + return cfd_eno3, cfd_weno3 +end + +function performEnoWenoAnalysis() + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + println("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + with_reconstruction(config_eno3, "eno", 3) # 显式指定 3 阶 + config_eno3.dt = 0.0025 # 覆盖默认值 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + run!(cfd_eno3) # 求解并生成 result 字典 + + # 3. 配置并运行 WENO3 求解 + println("Running WENO3 solver...") + config_weno3 = CfdConfig() + with_reconstruction(config_weno3, "weno", 3) # 显式指定 3 阶(WENO 默认 5 阶) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + run!(cfd_weno3) + + # 新增 WENO5 + println("Running WENO5 solver...") + + config_weno5 = CfdConfig() + with_reconstruction(config_weno5, "weno", 5) + config_weno5.dt = 0.0025 + config_weno5.rk_order = 2 + + cfd_weno5 = Cfd(config_weno5, mesh) + run!(cfd_weno5) + + println("Plotting comparison results...") + + plotter = CFDPlotter() + + # 绘图时加入 weno5 + plot_comparison( + plotter, + [cfd_eno3.result, cfd_weno3.result, cfd_weno5.result], + save_path="eno_weno_comparison.png" + ) + + return cfd_eno3, cfd_weno3, cfd_weno5 +end + +# 主程序入口 +if abspath(PROGRAM_FILE) == @__FILE__ + performEnoWenoAnalysis() + println("Analysis completed!") +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/boundary.jl b/example/1d-linear-convection/weno3/julia/03f/src/boundary.jl new file mode 100644 index 00000000..82f58b71 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/boundary.jl @@ -0,0 +1,121 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +#using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end + + +# ---------------------- BoundaryConditionFactory ---------------------- +module BoundaryConditionFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "边界条件 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + bc_type = cfd.config.boundary_type + if !(bc_type isa AbstractString) + error("boundary_type 必须为字符串,当前值: $bc_type") + end + + if !haskey(_REGISTRY, bc_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的边界条件: '$bc_type'。可用选项: $available") + end + + return _REGISTRY[bc_type](cfd) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("periodic", cfd -> Main.PeriodicBoundary(cfd)) +register("dirichlet", cfd -> Main.DirichletBoundary(cfd)) +register("neumann", cfd -> Main.NeumannBoundary(cfd)) + +end # module BoundaryConditionFactory + +export BoundaryConditionFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/config.jl b/example/1d-linear-convection/weno3/julia/03f/src/config.jl new file mode 100644 index 00000000..aaa4bdb5 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/config.jl @@ -0,0 +1,74 @@ +# julia/config.jl +""" +CfdConfig:与 Python config.py 完全同构 +""" +mutable struct CfdConfig + ic_type::String + recon_scheme::String + flux_type::String + rk_order::Int + wave_speed::Float64 + final_time::Float64 + dt::Float64 + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + spatial_order::Int + equation_type::String # ← 新增 + problem_type::String # ← 新增 + + function CfdConfig() + new( + "step", + "eno", + "rusanov", + #"engquist-osher", + 1, + 1.0, + 0.625, + 0.025, + "periodic", + 1.0, + 2.0, + 2, + "linear_advection", # equation_type + "linear_advection" # problem_type + ) + end +end + +""" +专用配置:重建方案(链式调用) +""" +function with_reconstruction(cfg::CfdConfig, scheme::String, order::Union{Int, Nothing}=nothing) + cfg.recon_scheme = lowercase(scheme) + + if order !== nothing + cfg.spatial_order = order + else + if startswith(cfg.recon_scheme, "weno") + cfg.spatial_order = 5 + elseif cfg.recon_scheme == "eno" + cfg.spatial_order = 3 + else + error("不支持的重建格式:$scheme(仅支持 eno/weno)") + end + end + + return cfg # 支持链式调用 +end + +""" +专用配置:边界条件(链式调用) +""" +function with_boundary(cfg::CfdConfig, bc_type::String; left_value=nothing, right_value=nothing) + cfg.boundary_type = bc_type + if left_value !== nothing + cfg.left_boundary_value = left_value + end + if right_value !== nothing + cfg.right_boundary_value = right_value + end + return cfg +end + diff --git a/example/1d-linear-convection/weno3/julia/03f/src/domain.jl b/example/1d-linear-convection/weno3/julia/03f/src/domain.jl new file mode 100644 index 00000000..eec26114 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/domain.jl @@ -0,0 +1,58 @@ +# julia/domain.jl +""" +Domain 结构体(与 domain.py 完全同构) +- 保存 config +- nghosts 计算逻辑 1:1 +- ist/ied 为 0-based(与 Python 一致) +""" + +include("mesh.jl") +include("utils.jl") + +mutable struct Domain + config::Any # 保存 config(与 Python 一致) + mesh::Mesh + nghosts::Int + ist::Int # 0-based + ied::Int # 0-based + ntcells::Int +end + +function Domain(config::Any, mesh::Mesh) + nghosts = _calc_nghosts(config) + ist = nghosts + 1 # ← 1-based 起始索引 + ied = ist + mesh.ncells # ← 1-based 结束索引(不包含) + ntcells = mesh.ncells + 2 * nghosts + Domain(config, mesh, nghosts, ist, ied, ntcells) +end + +function _calc_nghosts(config::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + order = getfield_safe(config, :spatial_order, 2) + + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + if scheme == "eno" + nghosts = order + elseif startswith(scheme, "weno") + nghosts = order ÷ 2 + 1 + else + error("未知重建格式 $(scheme),无法计算ghost层!") + end + + if nghosts <= 0 + error("计算得到的ghost层数量无效:$(nghosts)(阶数$(order),格式$(scheme))") + end + + return nghosts +end + +function is_physical_cell(domain::Domain, idx::Int) + return domain.ist <= idx < domain.ied +end + +function get_physical_indices(domain::Domain) + return domain.ist:(domain.ied - 1) +end diff --git a/example/1d-linear-convection/weno3/julia/03f/src/equations/base.jl b/example/1d-linear-convection/weno3/julia/03f/src/equations/base.jl new file mode 100644 index 00000000..1915d7e3 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/equations/base.jl @@ -0,0 +1,28 @@ +# src/equations/base.jl + +""" +抽象方程基类 +所有具体方程必须实现: +- eq_flux(eq, u) +- max_wave_speed(eq, u) +""" +abstract type Equation end + +""" +通量函数 F(u) +""" +function eq_flux(eq::Equation, u::Float64)::Float64 + error("Not implemented for $(typeof(eq))") +end + +""" +最大波速(用于 Rusanov 通量和 CFL) +""" +function max_wave_speed(eq::Equation, u::Float64)::Float64 + error("Not implemented for $(typeof(eq))") +end + +""" +方程变量数(标量方程返回 1) +""" +num_equations(eq::Equation) = 1 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/equations/equations.jl b/example/1d-linear-convection/weno3/julia/03f/src/equations/equations.jl new file mode 100644 index 00000000..de1195d4 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/equations/equations.jl @@ -0,0 +1,4 @@ +# src/equations/equations.jl + +include("base.jl") +include("linear_advection.jl") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/equations/linear_advection.jl b/example/1d-linear-convection/weno3/julia/03f/src/equations/linear_advection.jl new file mode 100644 index 00000000..3086c1ce --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/equations/linear_advection.jl @@ -0,0 +1,16 @@ +# src/equations/linear_advection.jl + +""" +线性对流方程: ∂u/∂t + c ∂u/∂x = 0 +""" +mutable struct LinearAdvectionEquation <: Equation + wave_speed::Float64 + function LinearAdvectionEquation(config::Any) + c = getfield_safe(config, :wave_speed, 1.0) + new(c) + end +end + +eq_flux(eq::LinearAdvectionEquation, u::Float64) = eq.wave_speed * u + +max_wave_speed(eq::LinearAdvectionEquation, u::Float64) = abs(eq.wave_speed) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/flux/base.jl b/example/1d-linear-convection/weno3/julia/03f/src/flux/base.jl new file mode 100644 index 00000000..58dd6cc9 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/flux/base.jl @@ -0,0 +1,7 @@ +# src/flux/base.jl +""" +抽象通量计算器接口 +Julia 无 ABC,用文档约定: +- 所有子类型必须实现 `compute!(calc, qL, qR, flux)` +""" +abstract type InviscidFluxCalculator end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/flux/engquist_osher.jl b/example/1d-linear-convection/weno3/julia/03f/src/flux/engquist_osher.jl new file mode 100644 index 00000000..c27b8165 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/flux/engquist_osher.jl @@ -0,0 +1,26 @@ +# src/flux/engquist_osher.jl +mutable struct EngquistOsherFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function EngquistOsherFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::EngquistOsherFluxCalculator, qL::Vector{Float64}, qR::Vector{Float64}, flux::Vector{Float64}) + eq = calc.cfd.equation # ← 从 cfd 获取 Equation + for i in 1:calc.mesh.nnodes + c = eq.wave_speed # ← 假设 LinearAdvectionEquation 有 .wave_speed 字段 + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = qL[i] + u_R = qR[i] + flux[i] = cp * u_L + cm * u_R + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/flux/factory.jl b/example/1d-linear-convection/weno3/julia/03f/src/flux/factory.jl new file mode 100644 index 00000000..6ae4917d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/flux/factory.jl @@ -0,0 +1,26 @@ +# src/flux/factory.jl + +module FluxCalculatorFactory + +# 全局注册表 +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "通量计算器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + flux_type = cfd.config.flux_type + if !haskey(_REGISTRY, flux_type) + error("未注册的通量计算器: '$flux_type'。可用: $(keys(_REGISTRY))") + end + return _REGISTRY[flux_type](cfd) +end + +register("rusanov", cfd -> Main.RusanovFluxCalculator(cfd)) +register("engquist-osher", cfd -> Main.EngquistOsherFluxCalculator(cfd)) + +end # module FluxCalculatorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/flux/flux.jl b/example/1d-linear-convection/weno3/julia/03f/src/flux/flux.jl new file mode 100644 index 00000000..533f034c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/flux/flux.jl @@ -0,0 +1,10 @@ +# src/flux/flux.jl + +# 加载组件(顺序很重要!) +include("base.jl") +include("rusanov.jl") +include("engquist_osher.jl") +include("factory.jl") + +# 导出(如果你未来用模块) +# export InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/flux/rusanov.jl b/example/1d-linear-convection/weno3/julia/03f/src/flux/rusanov.jl new file mode 100644 index 00000000..3908c453 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/flux/rusanov.jl @@ -0,0 +1,29 @@ +# src/flux/rusanov.jl + +mutable struct RusanovFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function RusanovFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::RusanovFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + eq = calc.cfd.equation + for i in 1:calc.mesh.nnodes + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = max_wave_speed(eq, u_L) + c_R = max_wave_speed(eq, u_R) + F_L = eq_flux(eq, u_L) + F_R = eq_flux(eq, u_R) + Smax = max(abs(c_L), abs(c_R)) + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/initial_condition.jl b/example/1d-linear-convection/weno3/julia/03f/src/initial_condition.jl new file mode 100644 index 00000000..1b9993aa --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/initial_condition.jl @@ -0,0 +1,112 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end + +# ---------------------- InitialConditionFactory ---------------------- +module InitialConditionFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "初始条件 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(ic_type::String, config::Any) + if !(ic_type isa AbstractString) + error("ic_type 必须为字符串,当前值: $ic_type") + end + + if !haskey(_REGISTRY, ic_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的初始条件: '$ic_type'。可用选项: $available") + end + + return _REGISTRY[ic_type](config) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("step", config -> Main.StepFunctionIC(config)) +register("sin", config -> Main.SineWaveIC(config)) +register("gaussian", config -> Main.GaussianPulseIC(config)) + +end # module InitialConditionFactory + +export InitialConditionFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/mesh.jl b/example/1d-linear-convection/weno3/julia/03f/src/mesh.jl new file mode 100644 index 00000000..1b0dd4bf --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/mesh.jl @@ -0,0 +1,45 @@ +# julia/mesh.jl +""" +Mesh 结构体(与提供的 mesh.py 完全同构) +- 字段名、初始化顺序、循环逻辑完全一致 +- 不做任何泛化或优化 +""" + +mutable struct Mesh + xmin::Float64 + xmax::Float64 + ncells::Int + nnodes::Int + nx::Int + x::Vector{Float64} + xcc::Vector{Float64} + L::Float64 # 在 init_mesh 中赋值 + dx::Float64 # 在 init_mesh 中赋值 + + function Mesh() + # 与 Python __init__ 完全一致 + xmin = 0.0 + xmax = 2.0 + ncells = 40 + nnodes = ncells + 1 + nx = ncells + x = zeros(Float64, nnodes) + xcc = zeros(Float64, ncells) + + # 调用 init_mesh(模拟 Python) + L = xmax - xmin + dx = L / ncells + + # Generate node coordinates: for i in range(self.nnodes) + for i in 1:nnodes + x[i] = xmin + (i - 1) * dx # Python i → Julia i-1 + end + + # Generate cell center coordinates + for i in 1:ncells + xcc[i] = 0.5 * (x[i] + x[i+1]) + end + + new(xmin, xmax, ncells, nnodes, nx, x, xcc, L, dx) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/plotter.jl b/example/1d-linear-convection/weno3/julia/03f/src/plotter.jl new file mode 100644 index 00000000..a77d8d68 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/plotter.jl @@ -0,0 +1,147 @@ +# julia/plotter.jl +""" +CFDPlotter 的 Julia 实现(通过 PythonCall.jl 调用 Matplotlib) +确保与 Python plotter.py 行为完全一致 +""" + +using PythonCall + +# 初始化 Python 环境(加载 matplotlib, inflect) +const plt = pyimport("matplotlib.pyplot") +const inflect = pyimport("inflect") + +mutable struct CFDPlotter + default_styles::Dict{String, Any} + p::Py +end + +function CFDPlotter() + default_styles = Dict{String, Any}( + "numerical" => Dict( + :color => "blue", + :linestyle => "-", + :marker => "o", + :markerfacecolor => "none" + ), + "analytical" => Dict( + :color => "red", + :linestyle => "--", + :marker => "", + :linewidth => 1.5 + ), + "comparison" => [ + Dict(:color => "black", :linestyle => "-", :marker => "o", :markerfacecolor => "none"), + Dict(:color => "blue", :linestyle => "--", :marker => "s", :markerfacecolor => "none"), + Dict(:color => "green", :linestyle => ":", :marker => "^", :markerfacecolor => "none") + ] + ) + p = inflect.engine() + CFDPlotter(default_styles, p) +end + +""" +轻量即时绘图(快速验证结果) +""" +function plot_quick(plotter::CFDPlotter, cfd_result::Dict; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + rk_order = cfd_result["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = cfd_result["config"]["final_time"] + order = cfd_result["config"]["order"] + scheme = uppercase(cfd_result["config"]["scheme"]) + actual_title = "1D Convection (t=$(final_time))\n$(order)th-order $(scheme) + $(rk_str)-order RK" + end + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"]; + label="Numerical ($(uppercase(cfd_result["config"]["scheme"])))", + plotter.default_styles["numerical"]..., + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + # 通用样式 + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +""" +多格式/多精度对比绘图 +""" +function plot_comparison(plotter::CFDPlotter, result_list::Vector{Dict{String, Any}}; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + schemes = [uppercase(r["config"]["scheme"]) * string(r["config"]["order"]) for r in result_list] + rk_order = result_list[1]["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = result_list[1]["config"]["final_time"] + actual_title = "1D Convection Comparison (t=$(final_time))\n$(join(schemes, ", ")) + $(rk_str)-order RK" + end + + # 绘制多个数值解 + for (i, res) in enumerate(result_list) + style = plotter.default_styles["comparison"][mod1(i, length(plotter.default_styles["comparison"]))] + label = "Numerical ($(uppercase(res["config"]["scheme"]))$(res["config"]["order"]))" + plt.plot( + res["x"], res["numerical"]; + label=label, + style..., + markersize=5, linewidth=0.5 + ) + end + + # 绘制解析解 + plt.plot( + result_list[1]["x"], result_list[1]["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +function _set_common_style(plotter::CFDPlotter, title::String) + plt.title(title, fontsize=12) + plt.xlabel("x", fontsize=10) + plt.ylabel("u", fontsize=10) + plt.legend(fontsize=9) + plt.grid(true, color="gray", linestyle="--", linewidth=0.5, alpha=0.7) + plt.tight_layout() +end + +""" +快捷函数:ENO/WENO对比绘图 +""" +function plot_eno_weno_comparison(eno_result::Dict, weno_result::Dict; save_path=nothing) + plotter = CFDPlotter() + plot_comparison(plotter, [eno_result, weno_result]; save_path=save_path) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/problems/base.jl b/example/1d-linear-convection/weno3/julia/03f/src/problems/base.jl new file mode 100644 index 00000000..e29d0fbb --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/problems/base.jl @@ -0,0 +1,22 @@ +# src/problems/base.jl + +""" +抽象问题基类 +所有具体问题必须实现: +- create_initial_condition(prob, config) +- create_boundary_condition(prob, cfd) +- exact_solution(prob, x, t) +""" +abstract type Problem end + +function create_initial_condition(prob::Problem, config::Any) + error("Not implemented for $(typeof(prob))") +end + +function create_boundary_condition(prob::Problem, cfd::Any) + error("Not implemented for $(typeof(prob))") +end + +function exact_solution(prob::Problem, x::Vector{Float64}, t::Float64)::Vector{Float64} + error("Not implemented for $(typeof(prob))") +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/problems/linear_advection.jl b/example/1d-linear-convection/weno3/julia/03f/src/problems/linear_advection.jl new file mode 100644 index 00000000..8b37d9f8 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/problems/linear_advection.jl @@ -0,0 +1,43 @@ +# src/problems/linear_advection.jl + +""" +线性对流问题:定义初始条件、边界条件、解析解 +""" +mutable struct LinearAdvectionProblem <: Problem + ic_type::String + boundary_type::String + left_value::Float64 + right_value::Float64 + domain_length::Float64 + wave_speed::Float64 # 从 config 获取,未来可从 equation 获取 + + function LinearAdvectionProblem(config::Any) + new( + getfield_safe(config, :ic_type, "step"), + getfield_safe(config, :boundary_type, "periodic"), + getfield_safe(config, :left_boundary_value, 1.0), + getfield_safe(config, :right_boundary_value, 2.0), + getfield_safe(config, :domain_length, 2.0), + getfield_safe(config, :wave_speed, 1.0) + ) + end +end + +function create_initial_condition(prob::LinearAdvectionProblem, config::Any) + return Main.InitialConditionFactory.create(prob.ic_type, config) +end + +function create_boundary_condition(prob::LinearAdvectionProblem, cfd::Any) + # 复用原有 BC 工厂(cfd 需包含 config + domain) + return Main.BoundaryConditionFactory.create(cfd) +end + +function exact_solution(prob::LinearAdvectionProblem, x::Vector{Float64}, t::Float64)::Vector{Float64} + L = prob.domain_length + c = prob.wave_speed + # 周期性平移 + x_shifted = @. (x - c * t + L) % L + # 重用 IC 的 evaluate_at + ic = Main.InitialConditionFactory.create(prob.ic_type, (ic_type=prob.ic_type,)) + return Main.evaluate_at(ic, x_shifted) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/problems/problems.jl b/example/1d-linear-convection/weno3/julia/03f/src/problems/problems.jl new file mode 100644 index 00000000..98311397 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/problems/problems.jl @@ -0,0 +1,4 @@ +# src/problems/problems.jl + +include("base.jl") +include("linear_advection.jl") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/reconstructor/eno.jl b/example/1d-linear-convection/weno3/julia/03f/src/reconstructor/eno.jl new file mode 100644 index 00000000..e78a636f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/reconstructor/eno.jl @@ -0,0 +1,107 @@ +# julia/reconstructor/eno.jl +""" +ENO 重构器(与 reconstructor/eno.py 完全同构) +""" + +# ---------------------- ENO 系数初始化 ---------------------- +function _init_eno_coef!(spatial_order::Int, coef::Matrix{Float64}) + if spatial_order == 1 + coef[1, 1] = 1.0 + coef[2, 1] = 1.0 + elseif spatial_order == 2 + coef[1, 1:2] = [3.0/2.0, -1.0/2.0] + coef[2, 1:2] = [1.0/2.0, 1.0/2.0] + coef[3, 1:2] = [-1.0/2.0, 3.0/2.0] + elseif spatial_order == 3 + coef[1, 1:3] = [11.0/6.0, -7.0/6.0, 1.0/3.0] + coef[2, 1:3] = [1.0/3.0, 5.0/6.0, -1.0/6.0] + coef[3, 1:3] = [-1.0/6.0, 5.0/6.0, 1.0/3.0] + coef[4, 1:3] = [1.0/3.0, -7.0/6.0, 11.0/6.0] + elseif spatial_order == 4 + coef[1, 1:4] = [25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0] + coef[2, 1:4] = [1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0] + coef[3, 1:4] = [-1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0] + coef[4, 1:4] = [1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0] + coef[5, 1:4] = [-1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0] + elseif spatial_order == 5 + coef[1, 1:5] = [137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0] + coef[2, 1:5] = [1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0] + coef[3, 1:5] = [-1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0] + coef[4, 1:5] = [1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0] + coef[5, 1:5] = [-1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0] + coef[6, 1:5] = [1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0] + elseif spatial_order == 6 + coef[1, 1:6] = [49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0] + coef[2, 1:6] = [1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0] + coef[3, 1:6] = [-1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0] + coef[4, 1:6] = [1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0] + coef[5, 1:6] = [-1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0] + coef[6, 1:6] = [1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0] + coef[7, 1:6] = [-1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0] + elseif spatial_order == 7 + coef[1, 1:7] = [363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0] + coef[2, 1:7] = [1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0] + coef[3, 1:7] = [-1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0] + coef[4, 1:7] = [1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0] + coef[5, 1:7] = [-1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0] + coef[6, 1:7] = [1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0] + coef[7, 1:7] = [-1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0] + coef[8, 1:7] = [1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0] + else + error("ENO 系数未实现 order=$spatial_order") + end +end + +# ---------------------- ENO 重构器 ---------------------- +mutable struct EnoReconstructor + spatial_order::Int + ntcells::Int + lmc::Vector{Int} + coef::Matrix{Float64} + dd::Matrix{Float64} + + function EnoReconstructor(spatial_order::Int, ntcells::Int) + lmc = zeros(Int, ntcells) + coef = zeros(Float64, spatial_order + 1, spatial_order) + dd = zeros(Float64, spatial_order, ntcells) + _init_eno_coef!(spatial_order, coef) + new(spatial_order, ntcells, lmc, coef, dd) + end +end + +function reconstruct(rec::EnoReconstructor, q::Vector{Float64}, cfd::Any) + # 1. 差商计算 (dd[1,:] = q) + @views rec.dd[1, :] .= q + for m in 2:rec.spatial_order + for j in 1:(rec.ntcells - m + 1) + rec.dd[m, j] = rec.dd[m-1, j+1] - rec.dd[m-1, j] + end + end + + # 2. 选择 smoothest stencil + domain = cfd.domain + for i in (domain.ist - 1):(domain.ied) # Python: range(ist-1, ied+1) → ied+1-1 = ied + rec.lmc[i] = i + for m in 2:rec.spatial_order + if abs(rec.dd[m, rec.lmc[i] - 1]) < abs(rec.dd[m, rec.lmc[i]]) + rec.lmc[i] -= 1 + end + end + end + + # 3. 重构界面值 + solution = cfd.solution + for i in domain.ist:(domain.ied) # Python: range(ist, ied+1) → ied+1-1 = ied + j = i - domain.ist + 1 # Julia 1-based + k1 = rec.lmc[i - 1] + k2 = rec.lmc[i] + r1 = (i - 1) - k1 + 1 + r2 = i - k2 + 1 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in 1:rec.spatial_order + solution.q_face_left[j] += q[k1 + m - 1] * rec.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m - 1] * rec.coef[r2, m] + end + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/reconstructor/factory.jl b/example/1d-linear-convection/weno3/julia/03f/src/reconstructor/factory.jl new file mode 100644 index 00000000..aef27365 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/reconstructor/factory.jl @@ -0,0 +1,46 @@ +# src/reconstructor/factory.jl + +module ReconstructorFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "重构器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(config::Any, domain::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + # 处理 WENO 默认命名 + if scheme == "weno" + order = getfield_safe(config, :spatial_order, 5) + scheme = "weno$(order)" + end + + if !haskey(_REGISTRY, scheme) + available = sort(collect(keys(_REGISTRY))) + error("未注册的重构器: '$scheme'。可用选项: $available") + end + + return _REGISTRY[scheme](config, domain) +end + +# ✅ 手动注册(与你原有风格一致) +register("eno", (config, domain) -> Main.EnoReconstructor(config.spatial_order, domain.ntcells)) +register("weno3", (config, domain) -> Main.Weno3Reconstructor()) +register("weno5", (config, domain) -> Main.Weno5Reconstructor()) # ← 新增 + +# ---------------------- 辅助函数 ---------------------- +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +end # module ReconstructorFactory + +export ReconstructorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/reconstructor/weno3.jl b/example/1d-linear-convection/weno3/julia/03f/src/reconstructor/weno3.jl new file mode 100644 index 00000000..e7067d57 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/reconstructor/weno3.jl @@ -0,0 +1,65 @@ +# julia/reconstructor/weno3.jl +""" +WENO3 重构器(与 reconstructor/weno3.py 完全同构) +""" + +mutable struct Weno3Reconstructor + # 无字段,与 Python 一致 +end + +function reconstruct(rec::Weno3Reconstructor, q::Vector{Float64}, cfd::Any) + domain = cfd.domain + solution = cfd.solution + _reconstruct_left_interfaces_weno3(domain, q, solution.q_face_left) + _reconstruct_right_interfaces_weno3(domain, q, solution.q_face_right) +end + +function _reconstruct_left_interfaces_weno3(domain, u, qL) + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in (domain.ist - 1):(domain.ied - 1) + j = i - (domain.ist - 1) + 1 # ← Julia 1-based: j = i - (ist-1) 对应 Python j = i - (ist-1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = _reconstruct_from_left_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_right_interfaces_weno3(domain, u, qR) + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in domain.ist:domain.ied + j = i - domain.ist + 1 # ← Julia 1-based: j = i - ist 对应 Python j = i - ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = _reconstruct_from_right_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_from_left_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -0.5*v1 + 1.5*v2 # r=1 stencil + q1 = 0.5*v2 + 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end + +function _reconstruct_from_right_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 0.5*v1 + 0.5*v2 # r=1 stencil + q1 = 1.5*v2 - 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/reconstructor/weno5.jl b/example/1d-linear-convection/weno3/julia/03f/src/reconstructor/weno5.jl new file mode 100644 index 00000000..8e5338bc --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/reconstructor/weno5.jl @@ -0,0 +1,77 @@ +# src/reconstructor/weno5.jl +""" +WENO5 重构器(与提供的 Python weno5.py 完全同构) +- 无字段(与 Weno3Reconstructor 一致) +- 所有逻辑 1:1 对齐 Python +""" + +mutable struct Weno5Reconstructor + # 无字段,与 Python class Weno5Reconstructor 一致 +end + +function reconstruct(rec::Weno5Reconstructor, q::Vector{Float64}, cfd::Any) + domain = cfd.domain + solution = cfd.solution + _reconstruct_left_interfaces_weno5(domain, q, solution.q_face_left) + _reconstruct_right_interfaces_weno5(domain, q, solution.q_face_right) +end + +function _reconstruct_left_interfaces_weno5(domain, u, qL) + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in (domain.ist - 1):(domain.ied - 1) + j = i - (domain.ist - 1) + 1 # Julia 1-based + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + qL[j] = _reconstruct_from_left_biased_stencil(v1, v2, v3, v4, v5) + end +end + +function _reconstruct_right_interfaces_weno5(domain, u, qR) + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in domain.ist:domain.ied + j = i - domain.ist + 1 # Julia 1-based + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + qR[j] = _reconstruct_from_right_biased_stencil(v1, v2, v3, v4, v5) + end +end + +function _reconstruct_from_left_biased_stencil(v1, v2, v3, v4, v5) + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)^2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)^2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)^2 + (1.0/4.0)*(v2 - v4)^2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)^2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)^2 + d0 = 1.0/10.0 + d1 = 3.0/5.0 + d2 = 3.0/10.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha2 = d2 / (eps + beta2)^2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = 1.0/3.0*v1 - 7.0/6.0*v2 + 11.0/6.0*v3 # r=2 + q1 = -1.0/6.0*v2 + 5.0/6.0*v3 + 1.0/3.0*v4 # r=1 + q2 = 1.0/3.0*v3 + 5.0/6.0*v4 - 1.0/6.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 +end + +function _reconstruct_from_right_biased_stencil(v1, v2, v3, v4, v5) + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)^2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)^2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)^2 + (1.0/4.0)*(v2 - v4)^2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)^2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)^2 + d0 = 3.0/10.0 + d1 = 3.0/5.0 + d2 = 1.0/10.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha2 = d2 / (eps + beta2)^2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = -1.0/6.0*v1 + 5.0/6.0*v2 + 1.0/3.0*v3 # r=2 + q1 = 1.0/3.0*v2 + 5.0/6.0*v3 - 1.0/6.0*v4 # r=1 + q2 = 11.0/6.0*v3 - 7.0/6.0*v4 + 1.0/3.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/registry.jl b/example/1d-linear-convection/weno3/julia/03f/src/registry.jl new file mode 100644 index 00000000..6acd9aaa --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/registry.jl @@ -0,0 +1,164 @@ +""" +统一注册系统 + 通用工厂 +- ComponentRegistry: 组件注册表 +- @register_component: 装饰器宏 +- BaseFactory: 通用工厂接口 +""" + +module ComponentRegistry + +# 注册表:Dict{category => Dict{name => constructor}} +const _REGISTRIES = Dict{String, Dict{String, Function}}() +const _VERBOSE = Ref(true) + +# ---------------------- 注册核心逻辑 ---------------------- +""" + register(category::String, name::String, ctor::Function) + +注册一个组件构造函数到指定类别。 +如果已存在同名组件且不同,则发出警告。 +""" +function register(category::String, name::String, ctor::Function) + if !haskey(_REGISTRIES, category) + _REGISTRIES[category] = Dict{String, Function}() + end + + registry = _REGISTRIES[category] + if haskey(registry, name) + if registry[name] !== ctor && _VERBOSE[] + @warn "覆盖注册: $category.$name" + end + end + + registry[name] = ctor + if _VERBOSE[] + println("✅ 已注册: $category.$name -> $(nameof(ctor))") + end +end + +""" + create(category::String, name::String, args...; kwargs...) + +从注册表创建组件实例。 +""" +function create(category::String, name::String, args...; kwargs...) + if !haskey(_REGISTRIES, category) + error("❌ 未知类别: $category (可用: $(collect(keys(_REGISTRIES))))") + end + + registry = _REGISTRIES[category] + lname = lowercase(name) + if !haskey(registry, lname) + available = sort(collect(keys(registry))) + error("❌ 未找到: $category.$name (可用: $available)") + end + + return registry[lname](args...; kwargs...) +end + +""" + list_all() + +返回所有已注册组件(按类别)。 +""" +function list_all() + return Dict(cat => sort(collect(keys(reg))) for (cat, reg) in _REGISTRIES) +end + +""" + set_verbose(flag::Bool) + +开启/关闭注册提示。 +""" +function set_verbose(flag::Bool) + _VERBOSE[] = flag +end + +end # module ComponentRegistry + + +# ---------------------- 装饰器宏:@register_component ---------------------- +""" +@register_component(category, [name]) + +用法: + @register_component("boundary", "periodic") + struct PeriodicBoundary ... + +若省略 name,则使用类型名的小写形式。 +""" +macro register_component(category::String, name_expr) + error("@register_component 需在类型定义前使用,且必须在模块顶层") +end + +macro register_component(category::String) + error("@register_component(category, name) 需指定 name 或在类型后使用") +end + +# 重载:@register_component("category", "name") struct X ... end +macro register_component(category::String, name::String, ex) + if !Meta.isexpr(ex, :struct) + error("@register_component 必须作用于 struct 定义") + end + + struct_name = ex.args[2] + if Meta.isexpr(struct_name, :curly) + struct_name = struct_name.args[1] + end + + # 插入注册调用(在模块顶层) + quote + $(esc(ex)) + $(ComponentRegistry).register($(category), $(name), $(esc(struct_name))) + end +end + +# 重载:@register_component("category") struct X ... end → name = lowercase(nameof(X)) +macro register_component(category::String, ex) + if !Meta.isexpr(ex, :struct) + error("@register_component 必须作用于 struct 定义") + end + + struct_name = ex.args[2] + if Meta.isexpr(struct_name, :curly) + struct_name = struct_name.args[1] + end + + name_str = string(struct_name) |> lowercase + + quote + $(esc(ex)) + $(ComponentRegistry).register($(category), $(name_str), $(esc(struct_name))) + end +end + + +# ---------------------- 通用工厂 ---------------------- +module BaseFactory + +using ..ComponentRegistry + +""" + create_component(category::String, name::String, args...; kwargs...) + +通用工厂接口,与 Python BaseFactory.create_component 行为一致。 +""" +function create_component(category::String, name::String, args...; kwargs...) + return ComponentRegistry.create(category, name, args...; kwargs...) +end + +""" + get_available_components(category::String) + +列出某类别下所有可用组件。 +""" +function get_available_components(category::String) + all = ComponentRegistry.list_all() + return get(all, category, String[]) +end + +end # module BaseFactory + + +# 导出接口 +export ComponentRegistry, BaseFactory, @register_component \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/residual.jl b/example/1d-linear-convection/weno3/julia/03f/src/residual.jl new file mode 100644 index 00000000..11a88593 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/residual.jl @@ -0,0 +1,69 @@ +# julia/residual.jl +""" +残差计算器(与 residual.py 完全同构) +- 封装重建→通量→散度完整流程 +- 依赖 cfd 的多个字段 +""" + +include("mesh.jl") + +mutable struct ResidualCalculator + cfd::Any + config::Any + domain::Any + solution::Any + mesh::Mesh + reconstructor::Any + flux_calculator::Any + + function ResidualCalculator(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + mesh = domain.mesh + reconstructor = cfd.reconstructor + + # ✅ 内部创建 flux_calculator(对标 Python) + flux_calculator = FluxCalculatorFactory.create(cfd) + + new(cfd, config, domain, solution, mesh, reconstructor, flux_calculator) + end +end + + +""" +计算完整残差(对外唯一接口) +""" +function compute!(calc::ResidualCalculator) + _reconstruct(calc) + _compute_inviscid_flux(calc) + _compute_flux_divergence(calc) +end + +""" +私有方法:界面值重建 +""" +function _reconstruct(calc::ResidualCalculator) + reconstruct(calc.reconstructor, calc.solution.u, calc.cfd) +end + +""" +私有方法:计算无粘通量 +""" +function _compute_inviscid_flux(calc::ResidualCalculator) + compute!(calc.flux_calculator, + calc.solution.q_face_left, + calc.solution.q_face_right, + calc.solution.flux) +end + +""" +私有方法:计算通量散度(残差 = -dF/dx) +""" +function _compute_flux_divergence(calc::ResidualCalculator) + solution = calc.solution + mesh = calc.mesh + for i in 1:mesh.ncells + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / mesh.dx + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/solution.jl b/example/1d-linear-convection/weno3/julia/03f/src/solution.jl new file mode 100644 index 00000000..7044c001 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/solution.jl @@ -0,0 +1,60 @@ +# julia/solution.jl +""" +Solution 结构体(与真实 solution.py 完全同构) +字段顺序、方法、逻辑 1:1 对齐 +""" + +include("mesh.jl") +include("domain.jl") +include("initial_condition.jl") + +mutable struct Solution + domain::Domain + q_face_left::Vector{Float64} + q_face_right::Vector{Float64} + flux::Vector{Float64} + res::Vector{Float64} + u::Vector{Float64} + un::Vector{Float64} + + function Solution(config::Any, domain::Domain) + mesh = domain.mesh + + q_face_left = zeros(Float64, mesh.nnodes) + q_face_right = zeros(Float64, mesh.nnodes) + flux = zeros(Float64, mesh.nnodes) + res = zeros(Float64, mesh.ncells) + u = zeros(Float64, domain.ntcells) + un = zeros(Float64, domain.ntcells) + + sol = new(domain, q_face_left, q_face_right, flux, res, u, un) + initialize_from_config(sol, config) + return sol + end +end + +""" +重置解数组为初始状态 +""" +function reset_solution(sol::Solution) + fill!(sol.u, 0.0) + fill!(sol.un, 0.0) +end + +""" +根据配置初始化场 +""" + +function initialize_from_config(sol::Solution, config::Any) + ic_type = getfield_safe(config, :ic_type, "step") + ic = InitialConditionFactory.create(ic_type, config) + apply(ic, sol) +end + +""" +更新旧场:un = u +""" +function update_old_field(sol::Solution) + sol.un .= sol.u # 等价于 Python 的 un[:] = u[:] +end + diff --git a/example/1d-linear-convection/weno3/julia/03f/src/solver.jl b/example/1d-linear-convection/weno3/julia/03f/src/solver.jl new file mode 100644 index 00000000..76a8132d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/solver.jl @@ -0,0 +1,163 @@ +# julia/solver.jl +""" +CFD 求解器主类(与 solver.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("initial_condition.jl") +include("boundary.jl") +include("flux/flux.jl") +include("residual.jl") +include("time_integration/time_integration.jl") +include("reconstructor/eno.jl") +include("reconstructor/weno3.jl") +include("reconstructor/weno5.jl") +include("reconstructor/factory.jl") + +include("equations/equations.jl") +include("problems/problems.jl") + +# 导入工厂模块(必须在顶层!) +using .FluxCalculatorFactory +using .BoundaryConditionFactory +using .ReconstructorFactory + +# ---------------------- Cfd 求解器 ---------------------- +mutable struct Cfd + config::Any + domain::Domain + solution::Solution + reconstructor::Any + residual_calculator::ResidualCalculator + integrator::Any + boundary_condition::Any + result::Dict{String, Any} + + function Cfd(config::Any, mesh::Mesh) + domain = Domain(config, mesh) + solution = Solution(config, domain) + reconstructor = ReconstructorFactory.create(config, domain) + + # 1. 创建 Equation 和 Problem + equation = create_equation(config) + problem = create_problem(config) + + # 2. 初始上下文(仅包含不依赖其他组件的字段) + full_cfd = ( + config = config, + domain = domain, + solution = solution, + reconstructor = reconstructor, + equation = equation, # ← 新增 + problem = problem # ← 新增 + ) + + # 3. 创建边界条件(通过 problem) + boundary_condition = create_boundary_condition(problem, full_cfd) + full_cfd = merge(full_cfd, (boundary_condition = boundary_condition,)) + + + # 4. 创建 residual_calculator(现在可访问 equation) + residual_calculator = ResidualCalculator(full_cfd) + full_cfd = merge(full_cfd, (residual_calculator = residual_calculator,)) + + # 5. 创建 integrator + integrator = TimeIntegratorFactory.create(full_cfd) + full_cfd = merge(full_cfd, (integrator = integrator,)) + + # 6. 注入完整 self + residual_calculator.cfd = full_cfd + integrator.base.cfd = full_cfd + + # 7. 初始化解(通过 problem) + ic = create_initial_condition(problem, config) + Main.apply(ic, solution) # 注意:apply 在 Main + + result = Dict{String, Any}() + new(config, domain, solution, reconstructor, residual_calculator, integrator, boundary_condition, result) + + end +end + +""" +通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界 +""" +function exact_solution(cfd::Cfd) + x = cfd.domain.mesh.xcc + T = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = @. (x - c * T + L) % L + + # ✅ 使用工厂创建初始条件(对标 Python) + ic = InitialConditionFactory.create(cfd.config.ic_type, cfd.config) + + return evaluate_at(ic, x_shifted) +end + +""" +主求解循环 +""" +function run!(cfd::Cfd) + # 应用初始边界条件并同步 old field + apply!(cfd.boundary_condition, cfd.solution.u) + update_old_field(cfd.solution) + + t = 0.0 + dt_old = cfd.config.dt + dt = dt_old + + while t < cfd.config.final_time + if t + dt > cfd.config.final_time + dt = cfd.config.final_time - t + end + #@show t, dt, maximum(cfd.solution.u), minimum(cfd.solution.u) + # 执行时间步 + step(cfd.integrator, dt) + t += dt + end + + # 恢复 dt + cfd.config.dt = dt_old + + # 整理结果 + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied-1] # Python: [ist:ied] + analytical = exact_solution(cfd) + + cfd.result = Dict( + "x" => cfd.domain.mesh.xcc, + "numerical" => u_numerical, + "analytical" => analytical, + "config" => Dict( + "scheme" => cfd.config.recon_scheme, + "order" => cfd.config.spatial_order, + "rk_order" => cfd.config.rk_order, + "final_time" => cfd.config.final_time + ) + ) + + return u_numerical +end + +# --- Equation / Problem 创建函数 --- +function create_equation(config::Any) + eq_type = config.equation_type + if eq_type == "linear_advection" + return LinearAdvectionEquation(config) + else + error("Unknown equation type: $eq_type") + end +end + +function create_problem(config::Any) + prob_type = config.problem_type + if prob_type == "linear_advection" + return LinearAdvectionProblem(config) + else + error("Unknown problem type: $prob_type") + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/time_integration/base.jl b/example/1d-linear-convection/weno3/julia/03f/src/time_integration/base.jl new file mode 100644 index 00000000..a5b93bc1 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/time_integration/base.jl @@ -0,0 +1,33 @@ +# src/time_integration/base.jl + +""" +公共基类逻辑(对应 Python TimeIntegratorBase) +""" + +mutable struct TimeIntegratorBase + cfd::Any + config::Any + domain::Domain + solution::Solution + residual_calculator::Any +end + +function TimeIntegratorBase(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + residual_calculator = cfd.residual_calculator + TimeIntegratorBase(cfd, config, domain, solution, residual_calculator) +end + +function compute_residual(integrator::TimeIntegratorBase) + compute!(integrator.residual_calculator) +end + +function apply_boundary(integrator::TimeIntegratorBase) + apply!(integrator.cfd.boundary_condition, integrator.solution.u) +end + +function map_idx(integrator::TimeIntegratorBase, i::Int) + return i - integrator.domain.ist + 1 +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/time_integration/factory.jl b/example/1d-linear-convection/weno3/julia/03f/src/time_integration/factory.jl new file mode 100644 index 00000000..271fcde5 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/time_integration/factory.jl @@ -0,0 +1,29 @@ +# src/time_integration/factory.jl + +module TimeIntegratorFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "时间积分器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + rk_order = cfd.config.rk_order + name = "rk$rk_order" + if !haskey(_REGISTRY, name) + available = sort(collect(keys(_REGISTRY))) + error("未注册的时间积分器: '$name'。可用选项: $available") + end + return _REGISTRY[name](cfd) +end + +# ✅ 关键:用 Main. 引用在 Main 模块中定义的类型 +register("rk1", cfd -> Main.RK1Integrator(cfd)) +register("rk2", cfd -> Main.RK2Integrator(cfd)) +register("rk3", cfd -> Main.RK3Integrator(cfd)) + +end # module TimeIntegratorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/time_integration/rk1.jl b/example/1d-linear-convection/weno3/julia/03f/src/time_integration/rk1.jl new file mode 100644 index 00000000..580db359 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/time_integration/rk1.jl @@ -0,0 +1,19 @@ +# src/time_integration/rk1.jl + +mutable struct RK1Integrator + base::TimeIntegratorBase +end + +function RK1Integrator(cfd::Any) + RK1Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK1Integrator, dt::Float64) + compute_residual(integrator.base) + for i in integrator.base.domain.ist:(integrator.base.domain.ied - 1) + j = map_idx(integrator.base, i) + integrator.base.solution.u[i] += dt * integrator.base.solution.res[j] + end + apply_boundary(integrator.base) + update_old_field(integrator.base.solution) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/time_integration/rk2.jl b/example/1d-linear-convection/weno3/julia/03f/src/time_integration/rk2.jl new file mode 100644 index 00000000..ca2b7ab6 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/time_integration/rk2.jl @@ -0,0 +1,30 @@ +# src/time_integration/rk2.jl + +mutable struct RK2Integrator + base::TimeIntegratorBase +end + +function RK2Integrator(cfd::Any) + RK2Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK2Integrator, dt::Float64) + base = integrator.base + compute_residual(base) + u_pred = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u_pred[i] += dt * base.solution.res[j] + end + base.solution.u .= u_pred + apply_boundary(base) + compute_residual(base) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = 0.5 * base.solution.un[i] + + 0.5 * base.solution.u[i] + + 0.5 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/time_integration/rk3.jl b/example/1d-linear-convection/weno3/julia/03f/src/time_integration/rk3.jl new file mode 100644 index 00000000..a8e07736 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/time_integration/rk3.jl @@ -0,0 +1,44 @@ +# src/time_integration/rk3.jl + +mutable struct RK3Integrator + base::TimeIntegratorBase +end + +function RK3Integrator(cfd::Any) + RK3Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK3Integrator, dt::Float64) + base = integrator.base + # Stage 1 + compute_residual(base) + u1 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u1[i] += dt * base.solution.res[j] + end + base.solution.u .= u1 + apply_boundary(base) + # Stage 2 + compute_residual(base) + u2 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u2[i] = 0.75 * base.solution.un[i] + + 0.25 * base.solution.u[i] + + 0.25 * dt * base.solution.res[j] + end + base.solution.u .= u2 + apply_boundary(base) + # Stage 3 + compute_residual(base) + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = c1 * base.solution.un[i] + + c2 * base.solution.u[i] + + c3 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/time_integration/time_integration.jl b/example/1d-linear-convection/weno3/julia/03f/src/time_integration/time_integration.jl new file mode 100644 index 00000000..4e60bfc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/time_integration/time_integration.jl @@ -0,0 +1,7 @@ +# src/time_integration/time_integration.jl + +include("base.jl") +include("rk1.jl") +include("rk2.jl") +include("rk3.jl") +include("factory.jl") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03f/src/utils.jl b/example/1d-linear-convection/weno3/julia/03f/src/utils.jl new file mode 100644 index 00000000..3786cd1a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03f/src/utils.jl @@ -0,0 +1,9 @@ +# src/utils.jl + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end diff --git a/example/1d-linear-convection/weno3/julia/03g/examples/run_eno_weno.jl b/example/1d-linear-convection/weno3/julia/03g/examples/run_eno_weno.jl new file mode 100644 index 00000000..f02110d9 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/examples/run_eno_weno.jl @@ -0,0 +1,99 @@ +# examples/run_eno_weno.jl +""" +1:1 复刻 run_eno_weno.py 的 Julia 版本 +""" + +include("../src/config.jl") +include("../src/mesh.jl") +include("../src/solver.jl") +include("../src/plotter.jl") + +function performEnoWenoAnalysisBAK() + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + println("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + with_reconstruction(config_eno3, "eno", 3) # 显式指定 3 阶 + config_eno3.dt = 0.0025 # 覆盖默认值 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + run!(cfd_eno3) # 求解并生成 result 字典 + + # 3. 配置并运行 WENO3 求解 + println("Running WENO3 solver...") + config_weno3 = CfdConfig() + with_reconstruction(config_weno3, "weno", 3) # 显式指定 3 阶(WENO 默认 5 阶) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + run!(cfd_weno3) + + # 5. 绘制 ENO/WENO 对比图 + println("Plotting comparison results...") + plot_eno_weno_comparison( + cfd_eno3.result, + cfd_weno3.result; + save_path="eno_weno_comparison.png" + ) + + return cfd_eno3, cfd_weno3 +end + +function performEnoWenoAnalysis() + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + println("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + with_reconstruction(config_eno3, "eno", 3) # 显式指定 3 阶 + config_eno3.dt = 0.0025 # 覆盖默认值 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + run!(cfd_eno3) # 求解并生成 result 字典 + + # 3. 配置并运行 WENO3 求解 + println("Running WENO3 solver...") + config_weno3 = CfdConfig() + with_reconstruction(config_weno3, "weno", 3) # 显式指定 3 阶(WENO 默认 5 阶) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + run!(cfd_weno3) + + # 新增 WENO5 + println("Running WENO5 solver...") + + config_weno5 = CfdConfig() + with_reconstruction(config_weno5, "weno", 5) + config_weno5.dt = 0.0025 + config_weno5.rk_order = 2 + + cfd_weno5 = Cfd(config_weno5, mesh) + run!(cfd_weno5) + + println("Plotting comparison results...") + + plotter = CFDPlotter() + + # 绘图时加入 weno5 + plot_comparison( + plotter, + [cfd_eno3.result, cfd_weno3.result, cfd_weno5.result], + save_path="eno_weno_comparison.png" + ) + + return cfd_eno3, cfd_weno3, cfd_weno5 +end + +# 主程序入口 +if abspath(PROGRAM_FILE) == @__FILE__ + performEnoWenoAnalysis() + println("Analysis completed!") +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/boundary.jl b/example/1d-linear-convection/weno3/julia/03g/src/boundary.jl new file mode 100644 index 00000000..82f58b71 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/boundary.jl @@ -0,0 +1,121 @@ +# julia/boundary.jl +# 目标:与 Python boundary.py 行为完全一致(1:1 同构) +# 前提:ist/ied 在 Julia 中按 1-based 设置(ist = nghosts + 1) + +#using NPZ # 仅用于测试,实际模块可不依赖 + +# ---------------------- 抽象基类(Julia 风格:用函数接口) ---------------------- +# Julia 无 abstract class,用文档+约定 +# 所有子类型必须实现 apply!(bc, u) + +# ---------------------- PeriodicBoundary ---------------------- +mutable struct PeriodicBoundary + cfd::Any +end + +function apply!(self::PeriodicBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist # 1-based, e.g., 3 + ied = self.cfd.domain.ied # 1-based, e.g., 43 + + # 左 ghost: u[ist - 1 - ig] = u[ied - 1 - ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ied - 1 - ig] + end + # 右 ghost: u[ied + ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ied + ig] = u[ist + ig] + end +end + +# ---------------------- DirichletBoundary ---------------------- +mutable struct DirichletBoundary + cfd::Any +end + +function apply!(self::DirichletBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + left_value = getfield_safe(self.cfd.config, :left_boundary_value, 1.0) + right_value = getfield_safe(self.cfd.config, :right_boundary_value, 2.0) + + # 左边界 + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = left_value + end + # 右边界 + for ig in 0:(nghosts-1) + u[ied + ig] = right_value + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Dirichlet边界: 左值=$left_value, 右值=$right_value") + end +end + +# ---------------------- NeumannBoundary ---------------------- +mutable struct NeumannBoundary + cfd::Any +end + +function apply!(self::NeumannBoundary, u::Vector{Float64}) + nghosts = self.cfd.domain.nghosts + ist = self.cfd.domain.ist + ied = self.cfd.domain.ied + + # 左边界零梯度:u[ist - 1 - ig] = u[ist + ig] + for ig in 0:(nghosts-1) + u[ist - 1 - ig] = u[ist + ig] + end + # 右边界零梯度:u[ied + ig] = u[ied - 1 - ig] ← 注意是 ied - 1 - ig + for ig in 0:(nghosts-1) + u[ied + ig] = u[ied - 1 - ig] + end + + # 调试信息 + if getfield_safe(self.cfd.config, :debug, false) + println(" 应用Neumann边界: 零梯度") + end +end + + +# ---------------------- BoundaryConditionFactory ---------------------- +module BoundaryConditionFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "边界条件 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + if !hasproperty(cfd, :config) + error("cfd 缺少 config 字段") + end + bc_type = cfd.config.boundary_type + if !(bc_type isa AbstractString) + error("boundary_type 必须为字符串,当前值: $bc_type") + end + + if !haskey(_REGISTRY, bc_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的边界条件: '$bc_type'。可用选项: $available") + end + + return _REGISTRY[bc_type](cfd) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("periodic", cfd -> Main.PeriodicBoundary(cfd)) +register("dirichlet", cfd -> Main.DirichletBoundary(cfd)) +register("neumann", cfd -> Main.NeumannBoundary(cfd)) + +end # module BoundaryConditionFactory + +export BoundaryConditionFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/config.jl b/example/1d-linear-convection/weno3/julia/03g/src/config.jl new file mode 100644 index 00000000..aaa4bdb5 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/config.jl @@ -0,0 +1,74 @@ +# julia/config.jl +""" +CfdConfig:与 Python config.py 完全同构 +""" +mutable struct CfdConfig + ic_type::String + recon_scheme::String + flux_type::String + rk_order::Int + wave_speed::Float64 + final_time::Float64 + dt::Float64 + boundary_type::String + left_boundary_value::Float64 + right_boundary_value::Float64 + spatial_order::Int + equation_type::String # ← 新增 + problem_type::String # ← 新增 + + function CfdConfig() + new( + "step", + "eno", + "rusanov", + #"engquist-osher", + 1, + 1.0, + 0.625, + 0.025, + "periodic", + 1.0, + 2.0, + 2, + "linear_advection", # equation_type + "linear_advection" # problem_type + ) + end +end + +""" +专用配置:重建方案(链式调用) +""" +function with_reconstruction(cfg::CfdConfig, scheme::String, order::Union{Int, Nothing}=nothing) + cfg.recon_scheme = lowercase(scheme) + + if order !== nothing + cfg.spatial_order = order + else + if startswith(cfg.recon_scheme, "weno") + cfg.spatial_order = 5 + elseif cfg.recon_scheme == "eno" + cfg.spatial_order = 3 + else + error("不支持的重建格式:$scheme(仅支持 eno/weno)") + end + end + + return cfg # 支持链式调用 +end + +""" +专用配置:边界条件(链式调用) +""" +function with_boundary(cfg::CfdConfig, bc_type::String; left_value=nothing, right_value=nothing) + cfg.boundary_type = bc_type + if left_value !== nothing + cfg.left_boundary_value = left_value + end + if right_value !== nothing + cfg.right_boundary_value = right_value + end + return cfg +end + diff --git a/example/1d-linear-convection/weno3/julia/03g/src/domain.jl b/example/1d-linear-convection/weno3/julia/03g/src/domain.jl new file mode 100644 index 00000000..eec26114 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/domain.jl @@ -0,0 +1,58 @@ +# julia/domain.jl +""" +Domain 结构体(与 domain.py 完全同构) +- 保存 config +- nghosts 计算逻辑 1:1 +- ist/ied 为 0-based(与 Python 一致) +""" + +include("mesh.jl") +include("utils.jl") + +mutable struct Domain + config::Any # 保存 config(与 Python 一致) + mesh::Mesh + nghosts::Int + ist::Int # 0-based + ied::Int # 0-based + ntcells::Int +end + +function Domain(config::Any, mesh::Mesh) + nghosts = _calc_nghosts(config) + ist = nghosts + 1 # ← 1-based 起始索引 + ied = ist + mesh.ncells # ← 1-based 结束索引(不包含) + ntcells = mesh.ncells + 2 * nghosts + Domain(config, mesh, nghosts, ist, ied, ntcells) +end + +function _calc_nghosts(config::Any) + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + order = getfield_safe(config, :spatial_order, 2) + + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + if scheme == "eno" + nghosts = order + elseif startswith(scheme, "weno") + nghosts = order ÷ 2 + 1 + else + error("未知重建格式 $(scheme),无法计算ghost层!") + end + + if nghosts <= 0 + error("计算得到的ghost层数量无效:$(nghosts)(阶数$(order),格式$(scheme))") + end + + return nghosts +end + +function is_physical_cell(domain::Domain, idx::Int) + return domain.ist <= idx < domain.ied +end + +function get_physical_indices(domain::Domain) + return domain.ist:(domain.ied - 1) +end diff --git a/example/1d-linear-convection/weno3/julia/03g/src/equations/base.jl b/example/1d-linear-convection/weno3/julia/03g/src/equations/base.jl new file mode 100644 index 00000000..1915d7e3 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/equations/base.jl @@ -0,0 +1,28 @@ +# src/equations/base.jl + +""" +抽象方程基类 +所有具体方程必须实现: +- eq_flux(eq, u) +- max_wave_speed(eq, u) +""" +abstract type Equation end + +""" +通量函数 F(u) +""" +function eq_flux(eq::Equation, u::Float64)::Float64 + error("Not implemented for $(typeof(eq))") +end + +""" +最大波速(用于 Rusanov 通量和 CFL) +""" +function max_wave_speed(eq::Equation, u::Float64)::Float64 + error("Not implemented for $(typeof(eq))") +end + +""" +方程变量数(标量方程返回 1) +""" +num_equations(eq::Equation) = 1 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/equations/equations.jl b/example/1d-linear-convection/weno3/julia/03g/src/equations/equations.jl new file mode 100644 index 00000000..de1195d4 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/equations/equations.jl @@ -0,0 +1,4 @@ +# src/equations/equations.jl + +include("base.jl") +include("linear_advection.jl") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/equations/linear_advection.jl b/example/1d-linear-convection/weno3/julia/03g/src/equations/linear_advection.jl new file mode 100644 index 00000000..3086c1ce --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/equations/linear_advection.jl @@ -0,0 +1,16 @@ +# src/equations/linear_advection.jl + +""" +线性对流方程: ∂u/∂t + c ∂u/∂x = 0 +""" +mutable struct LinearAdvectionEquation <: Equation + wave_speed::Float64 + function LinearAdvectionEquation(config::Any) + c = getfield_safe(config, :wave_speed, 1.0) + new(c) + end +end + +eq_flux(eq::LinearAdvectionEquation, u::Float64) = eq.wave_speed * u + +max_wave_speed(eq::LinearAdvectionEquation, u::Float64) = abs(eq.wave_speed) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/flux/base.jl b/example/1d-linear-convection/weno3/julia/03g/src/flux/base.jl new file mode 100644 index 00000000..58dd6cc9 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/flux/base.jl @@ -0,0 +1,7 @@ +# src/flux/base.jl +""" +抽象通量计算器接口 +Julia 无 ABC,用文档约定: +- 所有子类型必须实现 `compute!(calc, qL, qR, flux)` +""" +abstract type InviscidFluxCalculator end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/flux/engquist_osher.jl b/example/1d-linear-convection/weno3/julia/03g/src/flux/engquist_osher.jl new file mode 100644 index 00000000..c27b8165 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/flux/engquist_osher.jl @@ -0,0 +1,26 @@ +# src/flux/engquist_osher.jl +mutable struct EngquistOsherFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function EngquistOsherFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::EngquistOsherFluxCalculator, qL::Vector{Float64}, qR::Vector{Float64}, flux::Vector{Float64}) + eq = calc.cfd.equation # ← 从 cfd 获取 Equation + for i in 1:calc.mesh.nnodes + c = eq.wave_speed # ← 假设 LinearAdvectionEquation 有 .wave_speed 字段 + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = qL[i] + u_R = qR[i] + flux[i] = cp * u_L + cm * u_R + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/flux/factory.jl b/example/1d-linear-convection/weno3/julia/03g/src/flux/factory.jl new file mode 100644 index 00000000..6ae4917d --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/flux/factory.jl @@ -0,0 +1,26 @@ +# src/flux/factory.jl + +module FluxCalculatorFactory + +# 全局注册表 +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "通量计算器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + flux_type = cfd.config.flux_type + if !haskey(_REGISTRY, flux_type) + error("未注册的通量计算器: '$flux_type'。可用: $(keys(_REGISTRY))") + end + return _REGISTRY[flux_type](cfd) +end + +register("rusanov", cfd -> Main.RusanovFluxCalculator(cfd)) +register("engquist-osher", cfd -> Main.EngquistOsherFluxCalculator(cfd)) + +end # module FluxCalculatorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/flux/flux.jl b/example/1d-linear-convection/weno3/julia/03g/src/flux/flux.jl new file mode 100644 index 00000000..533f034c --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/flux/flux.jl @@ -0,0 +1,10 @@ +# src/flux/flux.jl + +# 加载组件(顺序很重要!) +include("base.jl") +include("rusanov.jl") +include("engquist_osher.jl") +include("factory.jl") + +# 导出(如果你未来用模块) +# export InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/flux/rusanov.jl b/example/1d-linear-convection/weno3/julia/03g/src/flux/rusanov.jl new file mode 100644 index 00000000..3908c453 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/flux/rusanov.jl @@ -0,0 +1,29 @@ +# src/flux/rusanov.jl + +mutable struct RusanovFluxCalculator <: InviscidFluxCalculator + cfd::Any + config::Any + mesh::Mesh + wave_speed::Float64 + + function RusanovFluxCalculator(cfd::Any) + config = cfd.config + mesh = cfd.domain.mesh + wave_speed = config.wave_speed + new(cfd, config, mesh, wave_speed) + end +end + +function compute!(calc::RusanovFluxCalculator, q_face_left::Vector{Float64}, q_face_right::Vector{Float64}, flux::Vector{Float64}) + eq = calc.cfd.equation + for i in 1:calc.mesh.nnodes + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = max_wave_speed(eq, u_L) + c_R = max_wave_speed(eq, u_R) + F_L = eq_flux(eq, u_L) + F_R = eq_flux(eq, u_R) + Smax = max(abs(c_L), abs(c_R)) + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/initial_condition.jl b/example/1d-linear-convection/weno3/julia/03g/src/initial_condition.jl new file mode 100644 index 00000000..1b9993aa --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/initial_condition.jl @@ -0,0 +1,112 @@ +# julia/initial_condition.jl +""" +初始条件模块(Julia 版) +目标:与 Python initial_condition.py 行为完全一致 +""" + +# ---------------------- 抽象接口(Julia 无继承,用函数约定) ---------------------- +# 所有初始条件必须实现: +# evaluate_at(ic, x::AbstractVector{Float64}) -> Vector{Float64} +# apply(ic, solution) + +# ---------------------- StepFunctionIC ---------------------- +struct StepFunctionIC + config::Any +end + +function evaluate_at(ic::StepFunctionIC, x::AbstractVector{Float64}) + u0 = ones(Float64, length(x)) + for i in eachindex(x) + if 0.5 <= x[i] <= 1.0 + u0[i] = 2.0 + end + end + return u0 +end + +function apply(ic::StepFunctionIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- SineWaveIC ---------------------- +struct SineWaveIC + config::Any +end + +function evaluate_at(ic::SineWaveIC, x::AbstractVector{Float64}) + L = getfield_safe(ic.config, :domain_length, 2.0) + return sin.(2π * x / L) +end + +function apply(ic::SineWaveIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- GaussianPulseIC ---------------------- +struct GaussianPulseIC + config::Any +end + +function evaluate_at(ic::GaussianPulseIC, x::AbstractVector{Float64}) + center = getfield_safe(ic.config, :pulse_center, 0.5) + width = getfield_safe(ic.config, :pulse_width, 0.1) + return exp.(-((x .- center) ./ width).^2) +end + +function apply(ic::GaussianPulseIC, solution) + x = solution.domain.mesh.xcc + u0 = evaluate_at(ic, x) + _apply_to_interior(solution, u0) +end + +# ---------------------- 公共辅助函数 ---------------------- +""" +将初始场 values 应用到 solution.u 的物理区域(含 ghost 的数组) +""" +function _apply_to_interior(solution, values) + domain = solution.domain + # Python: for i in range(domain.ist, domain.ied) + # Julia: for i in domain.ist:domain.ied-1 (因为 ied 是结束索引,不包含) + for i in domain.ist:(domain.ied - 1) + j = i - domain.ist + 1 # values 是 1-based,i - ist 是 0-based → +1 + solution.u[i] = values[j] + end +end + +# ---------------------- InitialConditionFactory ---------------------- +module InitialConditionFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "初始条件 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(ic_type::String, config::Any) + if !(ic_type isa AbstractString) + error("ic_type 必须为字符串,当前值: $ic_type") + end + + if !haskey(_REGISTRY, ic_type) + available = sort(collect(keys(_REGISTRY))) + error("未注册的初始条件: '$ic_type'。可用选项: $available") + end + + return _REGISTRY[ic_type](config) +end + +# ✅ 使用 Main. 前缀引用顶层类型 +register("step", config -> Main.StepFunctionIC(config)) +register("sin", config -> Main.SineWaveIC(config)) +register("gaussian", config -> Main.GaussianPulseIC(config)) + +end # module InitialConditionFactory + +export InitialConditionFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/mesh.jl b/example/1d-linear-convection/weno3/julia/03g/src/mesh.jl new file mode 100644 index 00000000..1b0dd4bf --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/mesh.jl @@ -0,0 +1,45 @@ +# julia/mesh.jl +""" +Mesh 结构体(与提供的 mesh.py 完全同构) +- 字段名、初始化顺序、循环逻辑完全一致 +- 不做任何泛化或优化 +""" + +mutable struct Mesh + xmin::Float64 + xmax::Float64 + ncells::Int + nnodes::Int + nx::Int + x::Vector{Float64} + xcc::Vector{Float64} + L::Float64 # 在 init_mesh 中赋值 + dx::Float64 # 在 init_mesh 中赋值 + + function Mesh() + # 与 Python __init__ 完全一致 + xmin = 0.0 + xmax = 2.0 + ncells = 40 + nnodes = ncells + 1 + nx = ncells + x = zeros(Float64, nnodes) + xcc = zeros(Float64, ncells) + + # 调用 init_mesh(模拟 Python) + L = xmax - xmin + dx = L / ncells + + # Generate node coordinates: for i in range(self.nnodes) + for i in 1:nnodes + x[i] = xmin + (i - 1) * dx # Python i → Julia i-1 + end + + # Generate cell center coordinates + for i in 1:ncells + xcc[i] = 0.5 * (x[i] + x[i+1]) + end + + new(xmin, xmax, ncells, nnodes, nx, x, xcc, L, dx) + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/plotter.jl b/example/1d-linear-convection/weno3/julia/03g/src/plotter.jl new file mode 100644 index 00000000..a77d8d68 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/plotter.jl @@ -0,0 +1,147 @@ +# julia/plotter.jl +""" +CFDPlotter 的 Julia 实现(通过 PythonCall.jl 调用 Matplotlib) +确保与 Python plotter.py 行为完全一致 +""" + +using PythonCall + +# 初始化 Python 环境(加载 matplotlib, inflect) +const plt = pyimport("matplotlib.pyplot") +const inflect = pyimport("inflect") + +mutable struct CFDPlotter + default_styles::Dict{String, Any} + p::Py +end + +function CFDPlotter() + default_styles = Dict{String, Any}( + "numerical" => Dict( + :color => "blue", + :linestyle => "-", + :marker => "o", + :markerfacecolor => "none" + ), + "analytical" => Dict( + :color => "red", + :linestyle => "--", + :marker => "", + :linewidth => 1.5 + ), + "comparison" => [ + Dict(:color => "black", :linestyle => "-", :marker => "o", :markerfacecolor => "none"), + Dict(:color => "blue", :linestyle => "--", :marker => "s", :markerfacecolor => "none"), + Dict(:color => "green", :linestyle => ":", :marker => "^", :markerfacecolor => "none") + ] + ) + p = inflect.engine() + CFDPlotter(default_styles, p) +end + +""" +轻量即时绘图(快速验证结果) +""" +function plot_quick(plotter::CFDPlotter, cfd_result::Dict; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + rk_order = cfd_result["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = cfd_result["config"]["final_time"] + order = cfd_result["config"]["order"] + scheme = uppercase(cfd_result["config"]["scheme"]) + actual_title = "1D Convection (t=$(final_time))\n$(order)th-order $(scheme) + $(rk_str)-order RK" + end + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"]; + label="Numerical ($(uppercase(cfd_result["config"]["scheme"])))", + plotter.default_styles["numerical"]..., + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + # 通用样式 + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +""" +多格式/多精度对比绘图 +""" +function plot_comparison(plotter::CFDPlotter, result_list::Vector{Dict{String, Any}}; title=nothing, show=true, save_path=nothing) + plt.figure("OneFLOW-CFD Solver", figsize=(10, 6)) + + # 自动生成标题 + actual_title = title + if actual_title === nothing + schemes = [uppercase(r["config"]["scheme"]) * string(r["config"]["order"]) for r in result_list] + rk_order = result_list[1]["config"]["rk_order"] + rk_str = plotter.p.ordinal(rk_order) + final_time = result_list[1]["config"]["final_time"] + actual_title = "1D Convection Comparison (t=$(final_time))\n$(join(schemes, ", ")) + $(rk_str)-order RK" + end + + # 绘制多个数值解 + for (i, res) in enumerate(result_list) + style = plotter.default_styles["comparison"][mod1(i, length(plotter.default_styles["comparison"]))] + label = "Numerical ($(uppercase(res["config"]["scheme"]))$(res["config"]["order"]))" + plt.plot( + res["x"], res["numerical"]; + label=label, + style..., + markersize=5, linewidth=0.5 + ) + end + + # 绘制解析解 + plt.plot( + result_list[1]["x"], result_list[1]["analytical"]; + label="Analytical", + plotter.default_styles["analytical"]... + ) + + _set_common_style(plotter, actual_title) + + if save_path !== nothing + plt.savefig(save_path, dpi=300, bbox_inches="tight") + end + if show + plt.show() + end + plt.close() +end + +function _set_common_style(plotter::CFDPlotter, title::String) + plt.title(title, fontsize=12) + plt.xlabel("x", fontsize=10) + plt.ylabel("u", fontsize=10) + plt.legend(fontsize=9) + plt.grid(true, color="gray", linestyle="--", linewidth=0.5, alpha=0.7) + plt.tight_layout() +end + +""" +快捷函数:ENO/WENO对比绘图 +""" +function plot_eno_weno_comparison(eno_result::Dict, weno_result::Dict; save_path=nothing) + plotter = CFDPlotter() + plot_comparison(plotter, [eno_result, weno_result]; save_path=save_path) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/problems/base.jl b/example/1d-linear-convection/weno3/julia/03g/src/problems/base.jl new file mode 100644 index 00000000..e29d0fbb --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/problems/base.jl @@ -0,0 +1,22 @@ +# src/problems/base.jl + +""" +抽象问题基类 +所有具体问题必须实现: +- create_initial_condition(prob, config) +- create_boundary_condition(prob, cfd) +- exact_solution(prob, x, t) +""" +abstract type Problem end + +function create_initial_condition(prob::Problem, config::Any) + error("Not implemented for $(typeof(prob))") +end + +function create_boundary_condition(prob::Problem, cfd::Any) + error("Not implemented for $(typeof(prob))") +end + +function exact_solution(prob::Problem, x::Vector{Float64}, t::Float64)::Vector{Float64} + error("Not implemented for $(typeof(prob))") +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/problems/linear_advection.jl b/example/1d-linear-convection/weno3/julia/03g/src/problems/linear_advection.jl new file mode 100644 index 00000000..8b37d9f8 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/problems/linear_advection.jl @@ -0,0 +1,43 @@ +# src/problems/linear_advection.jl + +""" +线性对流问题:定义初始条件、边界条件、解析解 +""" +mutable struct LinearAdvectionProblem <: Problem + ic_type::String + boundary_type::String + left_value::Float64 + right_value::Float64 + domain_length::Float64 + wave_speed::Float64 # 从 config 获取,未来可从 equation 获取 + + function LinearAdvectionProblem(config::Any) + new( + getfield_safe(config, :ic_type, "step"), + getfield_safe(config, :boundary_type, "periodic"), + getfield_safe(config, :left_boundary_value, 1.0), + getfield_safe(config, :right_boundary_value, 2.0), + getfield_safe(config, :domain_length, 2.0), + getfield_safe(config, :wave_speed, 1.0) + ) + end +end + +function create_initial_condition(prob::LinearAdvectionProblem, config::Any) + return Main.InitialConditionFactory.create(prob.ic_type, config) +end + +function create_boundary_condition(prob::LinearAdvectionProblem, cfd::Any) + # 复用原有 BC 工厂(cfd 需包含 config + domain) + return Main.BoundaryConditionFactory.create(cfd) +end + +function exact_solution(prob::LinearAdvectionProblem, x::Vector{Float64}, t::Float64)::Vector{Float64} + L = prob.domain_length + c = prob.wave_speed + # 周期性平移 + x_shifted = @. (x - c * t + L) % L + # 重用 IC 的 evaluate_at + ic = Main.InitialConditionFactory.create(prob.ic_type, (ic_type=prob.ic_type,)) + return Main.evaluate_at(ic, x_shifted) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/problems/problems.jl b/example/1d-linear-convection/weno3/julia/03g/src/problems/problems.jl new file mode 100644 index 00000000..98311397 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/problems/problems.jl @@ -0,0 +1,4 @@ +# src/problems/problems.jl + +include("base.jl") +include("linear_advection.jl") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/reconstructor/eno.jl b/example/1d-linear-convection/weno3/julia/03g/src/reconstructor/eno.jl new file mode 100644 index 00000000..5bcef6ab --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/reconstructor/eno.jl @@ -0,0 +1,114 @@ +# julia/reconstructor/eno.jl +""" +ENO 重构器(与 reconstructor/eno.py 完全同构) +""" + +# ---------------------- ENO 系数初始化 ---------------------- +function _init_eno_coef!(spatial_order::Int, coef::Matrix{Float64}) + if spatial_order == 1 + coef[1, 1] = 1.0 + coef[2, 1] = 1.0 + elseif spatial_order == 2 + coef[1, 1:2] = [3.0/2.0, -1.0/2.0] + coef[2, 1:2] = [1.0/2.0, 1.0/2.0] + coef[3, 1:2] = [-1.0/2.0, 3.0/2.0] + elseif spatial_order == 3 + coef[1, 1:3] = [11.0/6.0, -7.0/6.0, 1.0/3.0] + coef[2, 1:3] = [1.0/3.0, 5.0/6.0, -1.0/6.0] + coef[3, 1:3] = [-1.0/6.0, 5.0/6.0, 1.0/3.0] + coef[4, 1:3] = [1.0/3.0, -7.0/6.0, 11.0/6.0] + elseif spatial_order == 4 + coef[1, 1:4] = [25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0] + coef[2, 1:4] = [1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0] + coef[3, 1:4] = [-1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0] + coef[4, 1:4] = [1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0] + coef[5, 1:4] = [-1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0] + elseif spatial_order == 5 + coef[1, 1:5] = [137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0] + coef[2, 1:5] = [1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0] + coef[3, 1:5] = [-1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0] + coef[4, 1:5] = [1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0] + coef[5, 1:5] = [-1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0] + coef[6, 1:5] = [1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0] + elseif spatial_order == 6 + coef[1, 1:6] = [49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0] + coef[2, 1:6] = [1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0] + coef[3, 1:6] = [-1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0] + coef[4, 1:6] = [1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0] + coef[5, 1:6] = [-1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0] + coef[6, 1:6] = [1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0] + coef[7, 1:6] = [-1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0] + elseif spatial_order == 7 + coef[1, 1:7] = [363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0] + coef[2, 1:7] = [1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0] + coef[3, 1:7] = [-1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0] + coef[4, 1:7] = [1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0] + coef[5, 1:7] = [-1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0] + coef[6, 1:7] = [1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0] + coef[7, 1:7] = [-1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0] + coef[8, 1:7] = [1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0] + else + error("ENO 系数未实现 order=$spatial_order") + end +end + +# ---------------------- ENO 重构器 ---------------------- +mutable struct EnoReconstructor + cfd::Any + config::Any + domain::Any + spatial_order::Int + ntcells::Int + lmc::Vector{Int} + coef::Matrix{Float64} + dd::Matrix{Float64} + + function EnoReconstructor(cfd::Any) + config = cfd.config + domain = cfd.domain + spatial_order = config.spatial_order + ntcells = domain.ntcells + lmc = zeros(Int, ntcells) + coef = zeros(Float64, spatial_order + 1, spatial_order) + dd = zeros(Float64, spatial_order, ntcells) + _init_eno_coef!(spatial_order, coef) + new(cfd, config, domain, spatial_order, ntcells, lmc, coef, dd) + end +end + +function reconstruct(rec::EnoReconstructor, q::Vector{Float64}, cfd::Any) + # 1. 差商计算 (dd[1,:] = q) + @views rec.dd[1, :] .= q + for m in 2:rec.spatial_order + for j in 1:(rec.ntcells - m + 1) + rec.dd[m, j] = rec.dd[m-1, j+1] - rec.dd[m-1, j] + end + end + + # 2. 选择 smoothest stencil + domain = cfd.domain + for i in (domain.ist - 1):(domain.ied) # Python: range(ist-1, ied+1) → ied+1-1 = ied + rec.lmc[i] = i + for m in 2:rec.spatial_order + if abs(rec.dd[m, rec.lmc[i] - 1]) < abs(rec.dd[m, rec.lmc[i]]) + rec.lmc[i] -= 1 + end + end + end + + # 3. 重构界面值 + solution = cfd.solution + for i in domain.ist:(domain.ied) # Python: range(ist, ied+1) → ied+1-1 = ied + j = i - domain.ist + 1 # Julia 1-based + k1 = rec.lmc[i - 1] + k2 = rec.lmc[i] + r1 = (i - 1) - k1 + 1 + r2 = i - k2 + 1 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in 1:rec.spatial_order + solution.q_face_left[j] += q[k1 + m - 1] * rec.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m - 1] * rec.coef[r2, m] + end + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/reconstructor/factory.jl b/example/1d-linear-convection/weno3/julia/03g/src/reconstructor/factory.jl new file mode 100644 index 00000000..3b3f4ca8 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/reconstructor/factory.jl @@ -0,0 +1,50 @@ +# src/reconstructor/factory.jl + +module ReconstructorFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "重构器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + config = cfd.config + domain = cfd.domain + scheme = lowercase(string(getfield_safe(config, :recon_scheme, ""))) + + if scheme == "" + error("必须先通过 with_reconstruction 设置重建格式!") + end + + # 处理 WENO 默认命名 + if scheme == "weno" + order = getfield_safe(config, :spatial_order, 5) + scheme = "weno$(order)" + end + + if !haskey(_REGISTRY, scheme) + available = sort(collect(keys(_REGISTRY))) + error("未注册的重构器: '$scheme'。可用选项: $available") + end + + return _REGISTRY[scheme](cfd) # ✅ 只传 cfd +end + + +# ✅ 手动注册(与你原有风格一致) +register("eno", cfd -> Main.EnoReconstructor(cfd)) +register("weno3", cfd -> Main.Weno3Reconstructor(cfd)) +register("weno5", cfd -> Main.Weno5Reconstructor(cfd)) + +# ---------------------- 辅助函数 ---------------------- +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end + +end # module ReconstructorFactory + +export ReconstructorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/reconstructor/weno3.jl b/example/1d-linear-convection/weno3/julia/03g/src/reconstructor/weno3.jl new file mode 100644 index 00000000..f5d29490 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/reconstructor/weno3.jl @@ -0,0 +1,68 @@ +# julia/reconstructor/weno3.jl +""" +WENO3 重构器(与 reconstructor/weno3.py 完全同构) +""" + +mutable struct Weno3Reconstructor + cfd::Any + function Weno3Reconstructor(cfd::Any) + new(cfd) + end +end + +function reconstruct(rec::Weno3Reconstructor, q::Vector{Float64}, cfd::Any) + domain = cfd.domain + solution = cfd.solution + _reconstruct_left_interfaces_weno3(domain, q, solution.q_face_left) + _reconstruct_right_interfaces_weno3(domain, q, solution.q_face_right) +end + +function _reconstruct_left_interfaces_weno3(domain, u, qL) + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in (domain.ist - 1):(domain.ied - 1) + j = i - (domain.ist - 1) + 1 # ← Julia 1-based: j = i - (ist-1) 对应 Python j = i - (ist-1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = _reconstruct_from_left_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_right_interfaces_weno3(domain, u, qR) + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in domain.ist:domain.ied + j = i - domain.ist + 1 # ← Julia 1-based: j = i - ist 对应 Python j = i - ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = _reconstruct_from_right_biased_stencil(v1, v2, v3) + end +end + +function _reconstruct_from_left_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -0.5*v1 + 1.5*v2 # r=1 stencil + q1 = 0.5*v2 + 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end + +function _reconstruct_from_right_biased_stencil(v1, v2, v3) + eps = 1e-6 + beta0 = (v2 - v1)^2 + beta1 = (v3 - v2)^2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 0.5*v1 + 0.5*v2 # r=1 stencil + q1 = 1.5*v2 - 0.5*v3 # r=0 stencil + return w0 * q0 + w1 * q1 +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/reconstructor/weno5.jl b/example/1d-linear-convection/weno3/julia/03g/src/reconstructor/weno5.jl new file mode 100644 index 00000000..d1b5db0f --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/reconstructor/weno5.jl @@ -0,0 +1,80 @@ +# src/reconstructor/weno5.jl +""" +WENO5 重构器(与提供的 Python weno5.py 完全同构) +- 无字段(与 Weno3Reconstructor 一致) +- 所有逻辑 1:1 对齐 Python +""" + +mutable struct Weno5Reconstructor + cfd::Any + function Weno5Reconstructor(cfd::Any) + new(cfd) + end +end + +function reconstruct(rec::Weno5Reconstructor, q::Vector{Float64}, cfd::Any) + domain = cfd.domain + solution = cfd.solution + _reconstruct_left_interfaces_weno5(domain, q, solution.q_face_left) + _reconstruct_right_interfaces_weno5(domain, q, solution.q_face_right) +end + +function _reconstruct_left_interfaces_weno5(domain, u, qL) + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in (domain.ist - 1):(domain.ied - 1) + j = i - (domain.ist - 1) + 1 # Julia 1-based + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + qL[j] = _reconstruct_from_left_biased_stencil(v1, v2, v3, v4, v5) + end +end + +function _reconstruct_right_interfaces_weno5(domain, u, qR) + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in domain.ist:domain.ied + j = i - domain.ist + 1 # Julia 1-based + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + qR[j] = _reconstruct_from_right_biased_stencil(v1, v2, v3, v4, v5) + end +end + +function _reconstruct_from_left_biased_stencil(v1, v2, v3, v4, v5) + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)^2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)^2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)^2 + (1.0/4.0)*(v2 - v4)^2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)^2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)^2 + d0 = 1.0/10.0 + d1 = 3.0/5.0 + d2 = 3.0/10.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha2 = d2 / (eps + beta2)^2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = 1.0/3.0*v1 - 7.0/6.0*v2 + 11.0/6.0*v3 # r=2 + q1 = -1.0/6.0*v2 + 5.0/6.0*v3 + 1.0/3.0*v4 # r=1 + q2 = 1.0/3.0*v3 + 5.0/6.0*v4 - 1.0/6.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 +end + +function _reconstruct_from_right_biased_stencil(v1, v2, v3, v4, v5) + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)^2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)^2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)^2 + (1.0/4.0)*(v2 - v4)^2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)^2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)^2 + d0 = 3.0/10.0 + d1 = 3.0/5.0 + d2 = 1.0/10.0 + alpha0 = d0 / (eps + beta0)^2 + alpha1 = d1 / (eps + beta1)^2 + alpha2 = d2 / (eps + beta2)^2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = -1.0/6.0*v1 + 5.0/6.0*v2 + 1.0/3.0*v3 # r=2 + q1 = 1.0/3.0*v2 + 5.0/6.0*v3 - 1.0/6.0*v4 # r=1 + q2 = 11.0/6.0*v3 - 7.0/6.0*v4 + 1.0/3.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/registry.jl b/example/1d-linear-convection/weno3/julia/03g/src/registry.jl new file mode 100644 index 00000000..6acd9aaa --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/registry.jl @@ -0,0 +1,164 @@ +""" +统一注册系统 + 通用工厂 +- ComponentRegistry: 组件注册表 +- @register_component: 装饰器宏 +- BaseFactory: 通用工厂接口 +""" + +module ComponentRegistry + +# 注册表:Dict{category => Dict{name => constructor}} +const _REGISTRIES = Dict{String, Dict{String, Function}}() +const _VERBOSE = Ref(true) + +# ---------------------- 注册核心逻辑 ---------------------- +""" + register(category::String, name::String, ctor::Function) + +注册一个组件构造函数到指定类别。 +如果已存在同名组件且不同,则发出警告。 +""" +function register(category::String, name::String, ctor::Function) + if !haskey(_REGISTRIES, category) + _REGISTRIES[category] = Dict{String, Function}() + end + + registry = _REGISTRIES[category] + if haskey(registry, name) + if registry[name] !== ctor && _VERBOSE[] + @warn "覆盖注册: $category.$name" + end + end + + registry[name] = ctor + if _VERBOSE[] + println("✅ 已注册: $category.$name -> $(nameof(ctor))") + end +end + +""" + create(category::String, name::String, args...; kwargs...) + +从注册表创建组件实例。 +""" +function create(category::String, name::String, args...; kwargs...) + if !haskey(_REGISTRIES, category) + error("❌ 未知类别: $category (可用: $(collect(keys(_REGISTRIES))))") + end + + registry = _REGISTRIES[category] + lname = lowercase(name) + if !haskey(registry, lname) + available = sort(collect(keys(registry))) + error("❌ 未找到: $category.$name (可用: $available)") + end + + return registry[lname](args...; kwargs...) +end + +""" + list_all() + +返回所有已注册组件(按类别)。 +""" +function list_all() + return Dict(cat => sort(collect(keys(reg))) for (cat, reg) in _REGISTRIES) +end + +""" + set_verbose(flag::Bool) + +开启/关闭注册提示。 +""" +function set_verbose(flag::Bool) + _VERBOSE[] = flag +end + +end # module ComponentRegistry + + +# ---------------------- 装饰器宏:@register_component ---------------------- +""" +@register_component(category, [name]) + +用法: + @register_component("boundary", "periodic") + struct PeriodicBoundary ... + +若省略 name,则使用类型名的小写形式。 +""" +macro register_component(category::String, name_expr) + error("@register_component 需在类型定义前使用,且必须在模块顶层") +end + +macro register_component(category::String) + error("@register_component(category, name) 需指定 name 或在类型后使用") +end + +# 重载:@register_component("category", "name") struct X ... end +macro register_component(category::String, name::String, ex) + if !Meta.isexpr(ex, :struct) + error("@register_component 必须作用于 struct 定义") + end + + struct_name = ex.args[2] + if Meta.isexpr(struct_name, :curly) + struct_name = struct_name.args[1] + end + + # 插入注册调用(在模块顶层) + quote + $(esc(ex)) + $(ComponentRegistry).register($(category), $(name), $(esc(struct_name))) + end +end + +# 重载:@register_component("category") struct X ... end → name = lowercase(nameof(X)) +macro register_component(category::String, ex) + if !Meta.isexpr(ex, :struct) + error("@register_component 必须作用于 struct 定义") + end + + struct_name = ex.args[2] + if Meta.isexpr(struct_name, :curly) + struct_name = struct_name.args[1] + end + + name_str = string(struct_name) |> lowercase + + quote + $(esc(ex)) + $(ComponentRegistry).register($(category), $(name_str), $(esc(struct_name))) + end +end + + +# ---------------------- 通用工厂 ---------------------- +module BaseFactory + +using ..ComponentRegistry + +""" + create_component(category::String, name::String, args...; kwargs...) + +通用工厂接口,与 Python BaseFactory.create_component 行为一致。 +""" +function create_component(category::String, name::String, args...; kwargs...) + return ComponentRegistry.create(category, name, args...; kwargs...) +end + +""" + get_available_components(category::String) + +列出某类别下所有可用组件。 +""" +function get_available_components(category::String) + all = ComponentRegistry.list_all() + return get(all, category, String[]) +end + +end # module BaseFactory + + +# 导出接口 +export ComponentRegistry, BaseFactory, @register_component \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/residual.jl b/example/1d-linear-convection/weno3/julia/03g/src/residual.jl new file mode 100644 index 00000000..11a88593 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/residual.jl @@ -0,0 +1,69 @@ +# julia/residual.jl +""" +残差计算器(与 residual.py 完全同构) +- 封装重建→通量→散度完整流程 +- 依赖 cfd 的多个字段 +""" + +include("mesh.jl") + +mutable struct ResidualCalculator + cfd::Any + config::Any + domain::Any + solution::Any + mesh::Mesh + reconstructor::Any + flux_calculator::Any + + function ResidualCalculator(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + mesh = domain.mesh + reconstructor = cfd.reconstructor + + # ✅ 内部创建 flux_calculator(对标 Python) + flux_calculator = FluxCalculatorFactory.create(cfd) + + new(cfd, config, domain, solution, mesh, reconstructor, flux_calculator) + end +end + + +""" +计算完整残差(对外唯一接口) +""" +function compute!(calc::ResidualCalculator) + _reconstruct(calc) + _compute_inviscid_flux(calc) + _compute_flux_divergence(calc) +end + +""" +私有方法:界面值重建 +""" +function _reconstruct(calc::ResidualCalculator) + reconstruct(calc.reconstructor, calc.solution.u, calc.cfd) +end + +""" +私有方法:计算无粘通量 +""" +function _compute_inviscid_flux(calc::ResidualCalculator) + compute!(calc.flux_calculator, + calc.solution.q_face_left, + calc.solution.q_face_right, + calc.solution.flux) +end + +""" +私有方法:计算通量散度(残差 = -dF/dx) +""" +function _compute_flux_divergence(calc::ResidualCalculator) + solution = calc.solution + mesh = calc.mesh + for i in 1:mesh.ncells + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / mesh.dx + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/solution.jl b/example/1d-linear-convection/weno3/julia/03g/src/solution.jl new file mode 100644 index 00000000..7044c001 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/solution.jl @@ -0,0 +1,60 @@ +# julia/solution.jl +""" +Solution 结构体(与真实 solution.py 完全同构) +字段顺序、方法、逻辑 1:1 对齐 +""" + +include("mesh.jl") +include("domain.jl") +include("initial_condition.jl") + +mutable struct Solution + domain::Domain + q_face_left::Vector{Float64} + q_face_right::Vector{Float64} + flux::Vector{Float64} + res::Vector{Float64} + u::Vector{Float64} + un::Vector{Float64} + + function Solution(config::Any, domain::Domain) + mesh = domain.mesh + + q_face_left = zeros(Float64, mesh.nnodes) + q_face_right = zeros(Float64, mesh.nnodes) + flux = zeros(Float64, mesh.nnodes) + res = zeros(Float64, mesh.ncells) + u = zeros(Float64, domain.ntcells) + un = zeros(Float64, domain.ntcells) + + sol = new(domain, q_face_left, q_face_right, flux, res, u, un) + initialize_from_config(sol, config) + return sol + end +end + +""" +重置解数组为初始状态 +""" +function reset_solution(sol::Solution) + fill!(sol.u, 0.0) + fill!(sol.un, 0.0) +end + +""" +根据配置初始化场 +""" + +function initialize_from_config(sol::Solution, config::Any) + ic_type = getfield_safe(config, :ic_type, "step") + ic = InitialConditionFactory.create(ic_type, config) + apply(ic, sol) +end + +""" +更新旧场:un = u +""" +function update_old_field(sol::Solution) + sol.un .= sol.u # 等价于 Python 的 un[:] = u[:] +end + diff --git a/example/1d-linear-convection/weno3/julia/03g/src/solver.jl b/example/1d-linear-convection/weno3/julia/03g/src/solver.jl new file mode 100644 index 00000000..c8c87416 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/solver.jl @@ -0,0 +1,165 @@ +# julia/solver.jl +""" +CFD 求解器主类(与 solver.py 完全同构) +""" + +include("mesh.jl") +include("domain.jl") +include("solution.jl") +include("initial_condition.jl") +include("boundary.jl") +include("flux/flux.jl") +include("residual.jl") +include("time_integration/time_integration.jl") +include("reconstructor/eno.jl") +include("reconstructor/weno3.jl") +include("reconstructor/weno5.jl") +include("reconstructor/factory.jl") + +include("equations/equations.jl") +include("problems/problems.jl") + +# 导入工厂模块(必须在顶层!) +using .FluxCalculatorFactory +using .BoundaryConditionFactory +using .ReconstructorFactory + +# ---------------------- Cfd 求解器 ---------------------- +mutable struct Cfd + config::Any + domain::Domain + solution::Solution + reconstructor::Any + residual_calculator::ResidualCalculator + integrator::Any + boundary_condition::Any + result::Dict{String, Any} + + function Cfd(config::Any, mesh::Mesh) + domain = Domain(config, mesh) + full_cfd = ( + config = config, + domain = domain + ) + + solution = Solution(config, domain) + reconstructor = ReconstructorFactory.create(full_cfd) + + full_cfd = merge(full_cfd, (; solution = solution, reconstructor = reconstructor)) + + + # 1. 创建 Equation 和 Problem + equation = create_equation(config) + problem = create_problem(config) + + # 2. 初始上下文(仅包含不依赖其他组件的字段 + full_cfd = merge(full_cfd, (;equation = equation)) + full_cfd = merge(full_cfd, (;problem = problem)) + + # 3. 创建边界条件(通过 problem) + boundary_condition = create_boundary_condition(problem, full_cfd) + full_cfd = merge(full_cfd, (boundary_condition = boundary_condition,)) + + + # 4. 创建 residual_calculator(现在可访问 equation) + residual_calculator = ResidualCalculator(full_cfd) + full_cfd = merge(full_cfd, (residual_calculator = residual_calculator,)) + + # 5. 创建 integrator + integrator = TimeIntegratorFactory.create(full_cfd) + full_cfd = merge(full_cfd, (integrator = integrator,)) + + # 6. 注入完整 self + residual_calculator.cfd = full_cfd + integrator.base.cfd = full_cfd + + # 7. 初始化解(通过 problem) + ic = create_initial_condition(problem, config) + Main.apply(ic, solution) # 注意:apply 在 Main + + result = Dict{String, Any}() + new(config, domain, solution, reconstructor, residual_calculator, integrator, boundary_condition, result) + + end +end + +""" +通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界 +""" +function exact_solution(cfd::Cfd) + x = cfd.domain.mesh.xcc + T = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = @. (x - c * T + L) % L + + # ✅ 使用工厂创建初始条件(对标 Python) + ic = InitialConditionFactory.create(cfd.config.ic_type, cfd.config) + + return evaluate_at(ic, x_shifted) +end + +""" +主求解循环 +""" +function run!(cfd::Cfd) + # 应用初始边界条件并同步 old field + apply!(cfd.boundary_condition, cfd.solution.u) + update_old_field(cfd.solution) + + t = 0.0 + dt_old = cfd.config.dt + dt = dt_old + + while t < cfd.config.final_time + if t + dt > cfd.config.final_time + dt = cfd.config.final_time - t + end + #@show t, dt, maximum(cfd.solution.u), minimum(cfd.solution.u) + # 执行时间步 + step(cfd.integrator, dt) + t += dt + end + + # 恢复 dt + cfd.config.dt = dt_old + + # 整理结果 + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied-1] # Python: [ist:ied] + analytical = exact_solution(cfd) + + cfd.result = Dict( + "x" => cfd.domain.mesh.xcc, + "numerical" => u_numerical, + "analytical" => analytical, + "config" => Dict( + "scheme" => cfd.config.recon_scheme, + "order" => cfd.config.spatial_order, + "rk_order" => cfd.config.rk_order, + "final_time" => cfd.config.final_time + ) + ) + + return u_numerical +end + +# --- Equation / Problem 创建函数 --- +function create_equation(config::Any) + eq_type = config.equation_type + if eq_type == "linear_advection" + return LinearAdvectionEquation(config) + else + error("Unknown equation type: $eq_type") + end +end + +function create_problem(config::Any) + prob_type = config.problem_type + if prob_type == "linear_advection" + return LinearAdvectionProblem(config) + else + error("Unknown problem type: $prob_type") + end +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/time_integration/base.jl b/example/1d-linear-convection/weno3/julia/03g/src/time_integration/base.jl new file mode 100644 index 00000000..a5b93bc1 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/time_integration/base.jl @@ -0,0 +1,33 @@ +# src/time_integration/base.jl + +""" +公共基类逻辑(对应 Python TimeIntegratorBase) +""" + +mutable struct TimeIntegratorBase + cfd::Any + config::Any + domain::Domain + solution::Solution + residual_calculator::Any +end + +function TimeIntegratorBase(cfd::Any) + config = cfd.config + domain = cfd.domain + solution = cfd.solution + residual_calculator = cfd.residual_calculator + TimeIntegratorBase(cfd, config, domain, solution, residual_calculator) +end + +function compute_residual(integrator::TimeIntegratorBase) + compute!(integrator.residual_calculator) +end + +function apply_boundary(integrator::TimeIntegratorBase) + apply!(integrator.cfd.boundary_condition, integrator.solution.u) +end + +function map_idx(integrator::TimeIntegratorBase, i::Int) + return i - integrator.domain.ist + 1 +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/time_integration/factory.jl b/example/1d-linear-convection/weno3/julia/03g/src/time_integration/factory.jl new file mode 100644 index 00000000..271fcde5 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/time_integration/factory.jl @@ -0,0 +1,29 @@ +# src/time_integration/factory.jl + +module TimeIntegratorFactory + +const _REGISTRY = Dict{String, Function}() + +function register(name::String, ctor::Function) + if haskey(_REGISTRY, name) + @warn "时间积分器 '$name' 已注册,将被覆盖" + end + _REGISTRY[name] = ctor +end + +function create(cfd::Any) + rk_order = cfd.config.rk_order + name = "rk$rk_order" + if !haskey(_REGISTRY, name) + available = sort(collect(keys(_REGISTRY))) + error("未注册的时间积分器: '$name'。可用选项: $available") + end + return _REGISTRY[name](cfd) +end + +# ✅ 关键:用 Main. 引用在 Main 模块中定义的类型 +register("rk1", cfd -> Main.RK1Integrator(cfd)) +register("rk2", cfd -> Main.RK2Integrator(cfd)) +register("rk3", cfd -> Main.RK3Integrator(cfd)) + +end # module TimeIntegratorFactory \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/time_integration/rk1.jl b/example/1d-linear-convection/weno3/julia/03g/src/time_integration/rk1.jl new file mode 100644 index 00000000..580db359 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/time_integration/rk1.jl @@ -0,0 +1,19 @@ +# src/time_integration/rk1.jl + +mutable struct RK1Integrator + base::TimeIntegratorBase +end + +function RK1Integrator(cfd::Any) + RK1Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK1Integrator, dt::Float64) + compute_residual(integrator.base) + for i in integrator.base.domain.ist:(integrator.base.domain.ied - 1) + j = map_idx(integrator.base, i) + integrator.base.solution.u[i] += dt * integrator.base.solution.res[j] + end + apply_boundary(integrator.base) + update_old_field(integrator.base.solution) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/time_integration/rk2.jl b/example/1d-linear-convection/weno3/julia/03g/src/time_integration/rk2.jl new file mode 100644 index 00000000..ca2b7ab6 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/time_integration/rk2.jl @@ -0,0 +1,30 @@ +# src/time_integration/rk2.jl + +mutable struct RK2Integrator + base::TimeIntegratorBase +end + +function RK2Integrator(cfd::Any) + RK2Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK2Integrator, dt::Float64) + base = integrator.base + compute_residual(base) + u_pred = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u_pred[i] += dt * base.solution.res[j] + end + base.solution.u .= u_pred + apply_boundary(base) + compute_residual(base) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = 0.5 * base.solution.un[i] + + 0.5 * base.solution.u[i] + + 0.5 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/time_integration/rk3.jl b/example/1d-linear-convection/weno3/julia/03g/src/time_integration/rk3.jl new file mode 100644 index 00000000..a8e07736 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/time_integration/rk3.jl @@ -0,0 +1,44 @@ +# src/time_integration/rk3.jl + +mutable struct RK3Integrator + base::TimeIntegratorBase +end + +function RK3Integrator(cfd::Any) + RK3Integrator(TimeIntegratorBase(cfd)) +end + +function step(integrator::RK3Integrator, dt::Float64) + base = integrator.base + # Stage 1 + compute_residual(base) + u1 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u1[i] += dt * base.solution.res[j] + end + base.solution.u .= u1 + apply_boundary(base) + # Stage 2 + compute_residual(base) + u2 = copy(base.solution.u) + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + u2[i] = 0.75 * base.solution.un[i] + + 0.25 * base.solution.u[i] + + 0.25 * dt * base.solution.res[j] + end + base.solution.u .= u2 + apply_boundary(base) + # Stage 3 + compute_residual(base) + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in base.domain.ist:(base.domain.ied - 1) + j = map_idx(base, i) + base.solution.u[i] = c1 * base.solution.un[i] + + c2 * base.solution.u[i] + + c3 * dt * base.solution.res[j] + end + apply_boundary(base) + update_old_field(base.solution) +end \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/time_integration/time_integration.jl b/example/1d-linear-convection/weno3/julia/03g/src/time_integration/time_integration.jl new file mode 100644 index 00000000..4e60bfc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/time_integration/time_integration.jl @@ -0,0 +1,7 @@ +# src/time_integration/time_integration.jl + +include("base.jl") +include("rk1.jl") +include("rk2.jl") +include("rk3.jl") +include("factory.jl") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/julia/03g/src/utils.jl b/example/1d-linear-convection/weno3/julia/03g/src/utils.jl new file mode 100644 index 00000000..3786cd1a --- /dev/null +++ b/example/1d-linear-convection/weno3/julia/03g/src/utils.jl @@ -0,0 +1,9 @@ +# src/utils.jl + +# ---------------------- 辅助函数 ---------------------- +""" +模拟 Python 的 getattr(obj, name, default) +""" +function getfield_safe(obj, name::Symbol, default) + return isdefined(obj, name) ? getfield(obj, name) : default +end diff --git a/example/1d-linear-convection/weno3/python-v1/01/boundary.py b/example/1d-linear-convection/weno3/python-v1/01/boundary.py new file mode 100644 index 00000000..3a271aa2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/boundary.py @@ -0,0 +1,104 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + diff --git a/example/1d-linear-convection/weno3/python-v1/01/cfd_registry.py b/example/1d-linear-convection/weno3/python-v1/01/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01/config.py b/example/1d-linear-convection/weno3/python-v1/01/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01/domain.py b/example/1d-linear-convection/weno3/python-v1/01/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01/factories/base_factory.py b/example/1d-linear-convection/weno3/python-v1/01/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01/flux/__init__.py b/example/1d-linear-convection/weno3/python-v1/01/flux/__init__.py new file mode 100644 index 00000000..432a36cb --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/flux/__init__.py @@ -0,0 +1,7 @@ +# flux/__init__.py +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 确保子模块被导入以触发注册 +from . import rusanov +from . import engquist_osher \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01/flux/base.py b/example/1d-linear-convection/weno3/python-v1/01/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python-v1/01/flux/engquist_osher.py new file mode 100644 index 00000000..34ad5f9c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/flux/engquist_osher.py @@ -0,0 +1,19 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from cfd_registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01/flux/factory.py b/example/1d-linear-convection/weno3/python-v1/01/flux/factory.py new file mode 100644 index 00000000..c2f6f075 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/flux/factory.py @@ -0,0 +1,25 @@ +# flux/factory.py +""" +通量计算器专用工厂(封装注册细节,提供清晰接口) +符合你希望“将创建逻辑封装在工厂中”的设计原则 +""" + +from factories.base_factory import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01/flux/rusanov.py b/example/1d-linear-convection/weno3/python-v1/01/flux/rusanov.py new file mode 100644 index 00000000..62dd207c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/flux/rusanov.py @@ -0,0 +1,21 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from cfd_registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01/initial_condition.py b/example/1d-linear-convection/weno3/python-v1/01/initial_condition.py new file mode 100644 index 00000000..963e9cdd --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/initial_condition.py @@ -0,0 +1,107 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def exact_solution(self, cfd): + """ + 默认解析解:线性对流 u(x, t) = u0(x - c * t) + 子类可重写以支持更复杂物理 + """ + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * t + L) % L + return self.evaluate_at(x_shifted) + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', config.ic_type, config) + + @classmethod + def get_exact_solution(cls, cfd): + return cls.create(cfd.config).exact_solution(cfd) # 一行! + diff --git a/example/1d-linear-convection/weno3/python-v1/01/mesh.py b/example/1d-linear-convection/weno3/python-v1/01/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01/plotter.py b/example/1d-linear-convection/weno3/python-v1/01/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01/problem.py b/example/1d-linear-convection/weno3/python-v1/01/problem.py new file mode 100644 index 00000000..53719f26 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/problem.py @@ -0,0 +1,38 @@ +# problem.py +""" +问题定义模块:每个 Problem 子类代表一个完整测试用例 +包含初始条件、解析解(可选)、物理方程等 +""" + +from abc import ABC, abstractmethod +from initial_condition import InitialConditionFactory + + +class Problem(ABC): + """ + 抽象问题基类 + 每个具体问题(如线性对流、Sod 激波管)应继承此类 + """ + def __init__(self, config): + self.config = config + + @abstractmethod + def initial_condition(self): + """ + 返回 InitialCondition 实例 + """ + pass + + def exact_solution(self, cfd): + """ + 可选:返回解析解(数值数组) + 若无解析解,可抛出 NotImplementedError 或返回 None + """ + x = cfd.domain.mesh.xcc + raise NotImplementedError( + f"Problem '{self.__class__.__name__}' does not provide an exact solution." + ) + + @property + def name(self): + return self.__class__.__name__ \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01/problems/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/01/problems/linear_advection.py new file mode 100644 index 00000000..ea1e2df2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/problems/linear_advection.py @@ -0,0 +1,27 @@ +# problems/linear_advection.py +""" +线性对流问题:支持任意初始条件 + 周期平移解析解 +""" + +import numpy as np +from problem import Problem +from initial_condition import InitialConditionFactory + + +class LinearAdvectionProblem(Problem): + """ + 线性对流问题 u_t + c u_x = 0 + 解析解:u(x, t) = u0(x - c * t) + """ + + def initial_condition(self): + return InitialConditionFactory.create(self.config) + + def exact_solution(self, cfd): + ic = self.initial_condition() + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + x_shifted = (x - c * t + L) % L + return ic.evaluate_at(x_shifted) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python-v1/01/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01/reconstructor/base.py b/example/1d-linear-convection/weno3/python-v1/01/reconstructor/base.py new file mode 100644 index 00000000..094b4712 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def compute_face_values(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01/reconstructor/eno.py b/example/1d-linear-convection/weno3/python-v1/01/reconstructor/eno.py new file mode 100644 index 00000000..8fde4333 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def compute_face_values(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01/reconstructor/factory.py b/example/1d-linear-convection/weno3/python-v1/01/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python-v1/01/reconstructor/weno3.py new file mode 100644 index 00000000..929061c1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/reconstructor/weno3.py @@ -0,0 +1,59 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python-v1/01/registry.py b/example/1d-linear-convection/weno3/python-v1/01/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01/residual.py b/example/1d-linear-convection/weno3/python-v1/01/residual.py new file mode 100644 index 00000000..e0f96cd3 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux.factory import FluxCalculatorFactory +from reconstructor import ReconstructorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + + self.reconstructor = ReconstructorFactory.create(self.config, self.domain) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._compute_face_values() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _compute_face_values(self): + """私有方法:界面值重建""" + self.reconstructor.compute_face_values(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01/result.py b/example/1d-linear-convection/weno3/python-v1/01/result.py new file mode 100644 index 00000000..acd257b0 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/result.py @@ -0,0 +1,21 @@ +# result.py + +from initial_condition import InitialConditionFactory + +class ResultAssembler: + @staticmethod + def assemble(cfd: 'Cfd') -> dict: + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied].copy() + analytical = InitialConditionFactory.get_exact_solution(cfd) + + return { + "x": cfd.domain.mesh.xcc, + "numerical": u_numerical, + "analytical": analytical, + "config": { + "scheme": cfd.config.recon_scheme, + "order": cfd.config.spatial_order, + "rk_order": cfd.config.rk_order, + "final_time": cfd.config.final_time + } + } \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01/run_eno_weno.py b/example/1d-linear-convection/weno3/python-v1/01/run_eno_weno.py new file mode 100644 index 00000000..25efa62f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/run_eno_weno.py @@ -0,0 +1,52 @@ +# run_eno_weno.py + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +# ✅ 新增:导入 Problem +from problems.linear_advection import LinearAdvectionProblem + + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行 ENO3 求解 + print("Running ENO3 solver...") + config_eno3 = CfdConfig() + config_eno3.with_reconstruction("eno", 3) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno3 = LinearAdvectionProblem(config_eno3) + cfd_eno3 = Cfd(problem_eno3, mesh) # ← 注入 problem + cfd_eno3.run() + + # 3. 配置并运行 WENO3 求解 + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno3 = LinearAdvectionProblem(config_weno3) + cfd_weno3 = Cfd(problem_weno3, mesh) # ← 注入 problem + cfd_weno3.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" + ) + + +if __name__ == "__main__": + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01/solution.py b/example/1d-linear-convection/weno3/python-v1/01/solution.py new file mode 100644 index 00000000..3d4cba56 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01/solver.py b/example/1d-linear-convection/weno3/python-v1/01/solver.py new file mode 100644 index 00000000..32ffb15b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/solver.py @@ -0,0 +1,44 @@ +from boundary import BoundaryConditionFactory +from initial_condition import InitialConditionFactory +from time_integration import TimeIntegrator,TimeIntegratorFactory +from mesh import Mesh +from domain import Domain +from solution import Solution +from config import CfdConfig +from result import ResultAssembler +from problem import Problem + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, problem: Problem, mesh): + self.problem = problem # ← 核心:持有 Problem 实例 + self.config = problem.config # ← config 来自 problem + self.domain = Domain(problem.config, mesh) + self.solution = Solution(problem.config, self.domain) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + self.result = ResultAssembler.assemble(self) + return self.result["numerical"] + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01/time_integration.py b/example/1d-linear-convection/weno3/python-v1/01/time_integration.py new file mode 100644 index 00000000..51b2b20c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01/time_integration.py @@ -0,0 +1,126 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component +from residual import ResidualCalculator + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = ResidualCalculator(cfd) + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class TimeIntegratorFactory: + """时间推进器工厂""" + + @staticmethod + def create(cfd) -> 'TimeIntegrator': + """ + 创建时间推进器实例 + + Args: + cfd: CFD对象 + + Returns: + 时间推进器实例 + """ + from factories.base_factory import BaseFactory + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01a/boundary.py b/example/1d-linear-convection/weno3/python-v1/01a/boundary.py new file mode 100644 index 00000000..3a271aa2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/boundary.py @@ -0,0 +1,104 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + diff --git a/example/1d-linear-convection/weno3/python-v1/01a/cfd_registry.py b/example/1d-linear-convection/weno3/python-v1/01a/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01a/config.py b/example/1d-linear-convection/weno3/python-v1/01a/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01a/domain.py b/example/1d-linear-convection/weno3/python-v1/01a/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01a/factories/base_factory.py b/example/1d-linear-convection/weno3/python-v1/01a/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01a/flux/__init__.py b/example/1d-linear-convection/weno3/python-v1/01a/flux/__init__.py new file mode 100644 index 00000000..432a36cb --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/flux/__init__.py @@ -0,0 +1,7 @@ +# flux/__init__.py +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 确保子模块被导入以触发注册 +from . import rusanov +from . import engquist_osher \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01a/flux/base.py b/example/1d-linear-convection/weno3/python-v1/01a/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01a/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python-v1/01a/flux/engquist_osher.py new file mode 100644 index 00000000..34ad5f9c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/flux/engquist_osher.py @@ -0,0 +1,19 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from cfd_registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01a/flux/factory.py b/example/1d-linear-convection/weno3/python-v1/01a/flux/factory.py new file mode 100644 index 00000000..c2f6f075 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/flux/factory.py @@ -0,0 +1,25 @@ +# flux/factory.py +""" +通量计算器专用工厂(封装注册细节,提供清晰接口) +符合你希望“将创建逻辑封装在工厂中”的设计原则 +""" + +from factories.base_factory import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01a/flux/rusanov.py b/example/1d-linear-convection/weno3/python-v1/01a/flux/rusanov.py new file mode 100644 index 00000000..62dd207c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/flux/rusanov.py @@ -0,0 +1,21 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from cfd_registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01a/initial_condition.py b/example/1d-linear-convection/weno3/python-v1/01a/initial_condition.py new file mode 100644 index 00000000..963e9cdd --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/initial_condition.py @@ -0,0 +1,107 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def exact_solution(self, cfd): + """ + 默认解析解:线性对流 u(x, t) = u0(x - c * t) + 子类可重写以支持更复杂物理 + """ + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * t + L) % L + return self.evaluate_at(x_shifted) + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', config.ic_type, config) + + @classmethod + def get_exact_solution(cls, cfd): + return cls.create(cfd.config).exact_solution(cfd) # 一行! + diff --git a/example/1d-linear-convection/weno3/python-v1/01a/mesh.py b/example/1d-linear-convection/weno3/python-v1/01a/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01a/plotter.py b/example/1d-linear-convection/weno3/python-v1/01a/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01a/problem.py b/example/1d-linear-convection/weno3/python-v1/01a/problem.py new file mode 100644 index 00000000..53719f26 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/problem.py @@ -0,0 +1,38 @@ +# problem.py +""" +问题定义模块:每个 Problem 子类代表一个完整测试用例 +包含初始条件、解析解(可选)、物理方程等 +""" + +from abc import ABC, abstractmethod +from initial_condition import InitialConditionFactory + + +class Problem(ABC): + """ + 抽象问题基类 + 每个具体问题(如线性对流、Sod 激波管)应继承此类 + """ + def __init__(self, config): + self.config = config + + @abstractmethod + def initial_condition(self): + """ + 返回 InitialCondition 实例 + """ + pass + + def exact_solution(self, cfd): + """ + 可选:返回解析解(数值数组) + 若无解析解,可抛出 NotImplementedError 或返回 None + """ + x = cfd.domain.mesh.xcc + raise NotImplementedError( + f"Problem '{self.__class__.__name__}' does not provide an exact solution." + ) + + @property + def name(self): + return self.__class__.__name__ \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01a/problems/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/01a/problems/linear_advection.py new file mode 100644 index 00000000..ea1e2df2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/problems/linear_advection.py @@ -0,0 +1,27 @@ +# problems/linear_advection.py +""" +线性对流问题:支持任意初始条件 + 周期平移解析解 +""" + +import numpy as np +from problem import Problem +from initial_condition import InitialConditionFactory + + +class LinearAdvectionProblem(Problem): + """ + 线性对流问题 u_t + c u_x = 0 + 解析解:u(x, t) = u0(x - c * t) + """ + + def initial_condition(self): + return InitialConditionFactory.create(self.config) + + def exact_solution(self, cfd): + ic = self.initial_condition() + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + x_shifted = (x - c * t + L) % L + return ic.evaluate_at(x_shifted) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01a/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python-v1/01a/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01a/reconstructor/base.py b/example/1d-linear-convection/weno3/python-v1/01a/reconstructor/base.py new file mode 100644 index 00000000..094b4712 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def compute_face_values(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01a/reconstructor/eno.py b/example/1d-linear-convection/weno3/python-v1/01a/reconstructor/eno.py new file mode 100644 index 00000000..8fde4333 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def compute_face_values(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01a/reconstructor/factory.py b/example/1d-linear-convection/weno3/python-v1/01a/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01a/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python-v1/01a/reconstructor/weno3.py new file mode 100644 index 00000000..929061c1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/reconstructor/weno3.py @@ -0,0 +1,59 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python-v1/01a/registry.py b/example/1d-linear-convection/weno3/python-v1/01a/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01a/residual.py b/example/1d-linear-convection/weno3/python-v1/01a/residual.py new file mode 100644 index 00000000..e0f96cd3 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux.factory import FluxCalculatorFactory +from reconstructor import ReconstructorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + + self.reconstructor = ReconstructorFactory.create(self.config, self.domain) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._compute_face_values() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _compute_face_values(self): + """私有方法:界面值重建""" + self.reconstructor.compute_face_values(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01a/result.py b/example/1d-linear-convection/weno3/python-v1/01a/result.py new file mode 100644 index 00000000..acd257b0 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/result.py @@ -0,0 +1,21 @@ +# result.py + +from initial_condition import InitialConditionFactory + +class ResultAssembler: + @staticmethod + def assemble(cfd: 'Cfd') -> dict: + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied].copy() + analytical = InitialConditionFactory.get_exact_solution(cfd) + + return { + "x": cfd.domain.mesh.xcc, + "numerical": u_numerical, + "analytical": analytical, + "config": { + "scheme": cfd.config.recon_scheme, + "order": cfd.config.spatial_order, + "rk_order": cfd.config.rk_order, + "final_time": cfd.config.final_time + } + } \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01a/run_eno_weno.py b/example/1d-linear-convection/weno3/python-v1/01a/run_eno_weno.py new file mode 100644 index 00000000..25efa62f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/run_eno_weno.py @@ -0,0 +1,52 @@ +# run_eno_weno.py + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +# ✅ 新增:导入 Problem +from problems.linear_advection import LinearAdvectionProblem + + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行 ENO3 求解 + print("Running ENO3 solver...") + config_eno3 = CfdConfig() + config_eno3.with_reconstruction("eno", 3) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno3 = LinearAdvectionProblem(config_eno3) + cfd_eno3 = Cfd(problem_eno3, mesh) # ← 注入 problem + cfd_eno3.run() + + # 3. 配置并运行 WENO3 求解 + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno3 = LinearAdvectionProblem(config_weno3) + cfd_weno3 = Cfd(problem_weno3, mesh) # ← 注入 problem + cfd_weno3.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" + ) + + +if __name__ == "__main__": + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01a/solution.py b/example/1d-linear-convection/weno3/python-v1/01a/solution.py new file mode 100644 index 00000000..a0def7c3 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/solution.py @@ -0,0 +1,38 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01a/solver.py b/example/1d-linear-convection/weno3/python-v1/01a/solver.py new file mode 100644 index 00000000..bac8aeb3 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/solver.py @@ -0,0 +1,42 @@ +from boundary import BoundaryConditionFactory +from initial_condition import InitialConditionFactory +from time_integration import TimeIntegrator,TimeIntegratorFactory +from mesh import Mesh +from domain import Domain +from solution import Solution +from config import CfdConfig +from result import ResultAssembler +from problem import Problem + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, problem: Problem, mesh): + self.problem = problem # ← 核心:持有 Problem 实例 + self.config = problem.config # ← config 来自 problem + self.domain = Domain(problem.config, mesh) + self.solution = Solution(problem.config, self.domain) + self.integrator = TimeIntegratorFactory.create(self) + #self.boundary_condition = BoundaryConditionFactory.create(self) + + def run(self): + self.integrator.init_total_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + self.result = ResultAssembler.assemble(self) + return self.result["numerical"] + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01a/time_integration.py b/example/1d-linear-convection/weno3/python-v1/01a/time_integration.py new file mode 100644 index 00000000..a283a710 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01a/time_integration.py @@ -0,0 +1,132 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component +from residual import ResidualCalculator +from boundary import BoundaryConditionFactory + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = ResidualCalculator(cfd) + self.boundary_condition = BoundaryConditionFactory.create(cfd) + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + def init_total_field(self): + self.solution.initialize_from_config(self.config) + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class TimeIntegratorFactory: + """时间推进器工厂""" + + @staticmethod + def create(cfd) -> 'TimeIntegrator': + """ + 创建时间推进器实例 + + Args: + cfd: CFD对象 + + Returns: + 时间推进器实例 + """ + from factories.base_factory import BaseFactory + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01b/boundary.py b/example/1d-linear-convection/weno3/python-v1/01b/boundary.py new file mode 100644 index 00000000..3a271aa2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/boundary.py @@ -0,0 +1,104 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + diff --git a/example/1d-linear-convection/weno3/python-v1/01b/cfd_registry.py b/example/1d-linear-convection/weno3/python-v1/01b/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01b/config.py b/example/1d-linear-convection/weno3/python-v1/01b/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01b/domain.py b/example/1d-linear-convection/weno3/python-v1/01b/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01b/factories/base_factory.py b/example/1d-linear-convection/weno3/python-v1/01b/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01b/flux/__init__.py b/example/1d-linear-convection/weno3/python-v1/01b/flux/__init__.py new file mode 100644 index 00000000..432a36cb --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/flux/__init__.py @@ -0,0 +1,7 @@ +# flux/__init__.py +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 确保子模块被导入以触发注册 +from . import rusanov +from . import engquist_osher \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01b/flux/base.py b/example/1d-linear-convection/weno3/python-v1/01b/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01b/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python-v1/01b/flux/engquist_osher.py new file mode 100644 index 00000000..34ad5f9c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/flux/engquist_osher.py @@ -0,0 +1,19 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from cfd_registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01b/flux/factory.py b/example/1d-linear-convection/weno3/python-v1/01b/flux/factory.py new file mode 100644 index 00000000..c2f6f075 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/flux/factory.py @@ -0,0 +1,25 @@ +# flux/factory.py +""" +通量计算器专用工厂(封装注册细节,提供清晰接口) +符合你希望“将创建逻辑封装在工厂中”的设计原则 +""" + +from factories.base_factory import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01b/flux/rusanov.py b/example/1d-linear-convection/weno3/python-v1/01b/flux/rusanov.py new file mode 100644 index 00000000..62dd207c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/flux/rusanov.py @@ -0,0 +1,21 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from cfd_registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01b/initial_condition.py b/example/1d-linear-convection/weno3/python-v1/01b/initial_condition.py new file mode 100644 index 00000000..963e9cdd --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/initial_condition.py @@ -0,0 +1,107 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def exact_solution(self, cfd): + """ + 默认解析解:线性对流 u(x, t) = u0(x - c * t) + 子类可重写以支持更复杂物理 + """ + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * t + L) % L + return self.evaluate_at(x_shifted) + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', config.ic_type, config) + + @classmethod + def get_exact_solution(cls, cfd): + return cls.create(cfd.config).exact_solution(cfd) # 一行! + diff --git a/example/1d-linear-convection/weno3/python-v1/01b/mesh.py b/example/1d-linear-convection/weno3/python-v1/01b/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01b/plotter.py b/example/1d-linear-convection/weno3/python-v1/01b/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01b/problem.py b/example/1d-linear-convection/weno3/python-v1/01b/problem.py new file mode 100644 index 00000000..53719f26 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/problem.py @@ -0,0 +1,38 @@ +# problem.py +""" +问题定义模块:每个 Problem 子类代表一个完整测试用例 +包含初始条件、解析解(可选)、物理方程等 +""" + +from abc import ABC, abstractmethod +from initial_condition import InitialConditionFactory + + +class Problem(ABC): + """ + 抽象问题基类 + 每个具体问题(如线性对流、Sod 激波管)应继承此类 + """ + def __init__(self, config): + self.config = config + + @abstractmethod + def initial_condition(self): + """ + 返回 InitialCondition 实例 + """ + pass + + def exact_solution(self, cfd): + """ + 可选:返回解析解(数值数组) + 若无解析解,可抛出 NotImplementedError 或返回 None + """ + x = cfd.domain.mesh.xcc + raise NotImplementedError( + f"Problem '{self.__class__.__name__}' does not provide an exact solution." + ) + + @property + def name(self): + return self.__class__.__name__ \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01b/problems/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/01b/problems/linear_advection.py new file mode 100644 index 00000000..ea1e2df2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/problems/linear_advection.py @@ -0,0 +1,27 @@ +# problems/linear_advection.py +""" +线性对流问题:支持任意初始条件 + 周期平移解析解 +""" + +import numpy as np +from problem import Problem +from initial_condition import InitialConditionFactory + + +class LinearAdvectionProblem(Problem): + """ + 线性对流问题 u_t + c u_x = 0 + 解析解:u(x, t) = u0(x - c * t) + """ + + def initial_condition(self): + return InitialConditionFactory.create(self.config) + + def exact_solution(self, cfd): + ic = self.initial_condition() + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + x_shifted = (x - c * t + L) % L + return ic.evaluate_at(x_shifted) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01b/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python-v1/01b/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01b/reconstructor/base.py b/example/1d-linear-convection/weno3/python-v1/01b/reconstructor/base.py new file mode 100644 index 00000000..094b4712 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def compute_face_values(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01b/reconstructor/eno.py b/example/1d-linear-convection/weno3/python-v1/01b/reconstructor/eno.py new file mode 100644 index 00000000..8fde4333 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def compute_face_values(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01b/reconstructor/factory.py b/example/1d-linear-convection/weno3/python-v1/01b/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01b/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python-v1/01b/reconstructor/weno3.py new file mode 100644 index 00000000..929061c1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/reconstructor/weno3.py @@ -0,0 +1,59 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python-v1/01b/registry.py b/example/1d-linear-convection/weno3/python-v1/01b/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01b/residual.py b/example/1d-linear-convection/weno3/python-v1/01b/residual.py new file mode 100644 index 00000000..e0f96cd3 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux.factory import FluxCalculatorFactory +from reconstructor import ReconstructorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + + self.reconstructor = ReconstructorFactory.create(self.config, self.domain) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._compute_face_values() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _compute_face_values(self): + """私有方法:界面值重建""" + self.reconstructor.compute_face_values(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01b/result.py b/example/1d-linear-convection/weno3/python-v1/01b/result.py new file mode 100644 index 00000000..acd257b0 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/result.py @@ -0,0 +1,21 @@ +# result.py + +from initial_condition import InitialConditionFactory + +class ResultAssembler: + @staticmethod + def assemble(cfd: 'Cfd') -> dict: + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied].copy() + analytical = InitialConditionFactory.get_exact_solution(cfd) + + return { + "x": cfd.domain.mesh.xcc, + "numerical": u_numerical, + "analytical": analytical, + "config": { + "scheme": cfd.config.recon_scheme, + "order": cfd.config.spatial_order, + "rk_order": cfd.config.rk_order, + "final_time": cfd.config.final_time + } + } \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01b/run_eno_weno.py b/example/1d-linear-convection/weno3/python-v1/01b/run_eno_weno.py new file mode 100644 index 00000000..25efa62f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/run_eno_weno.py @@ -0,0 +1,52 @@ +# run_eno_weno.py + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +# ✅ 新增:导入 Problem +from problems.linear_advection import LinearAdvectionProblem + + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行 ENO3 求解 + print("Running ENO3 solver...") + config_eno3 = CfdConfig() + config_eno3.with_reconstruction("eno", 3) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno3 = LinearAdvectionProblem(config_eno3) + cfd_eno3 = Cfd(problem_eno3, mesh) # ← 注入 problem + cfd_eno3.run() + + # 3. 配置并运行 WENO3 求解 + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno3 = LinearAdvectionProblem(config_weno3) + cfd_weno3 = Cfd(problem_weno3, mesh) # ← 注入 problem + cfd_weno3.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" + ) + + +if __name__ == "__main__": + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01b/solution.py b/example/1d-linear-convection/weno3/python-v1/01b/solution.py new file mode 100644 index 00000000..90666c34 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/solution.py @@ -0,0 +1,32 @@ +# solution.py +import numpy as np + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01b/solver.py b/example/1d-linear-convection/weno3/python-v1/01b/solver.py new file mode 100644 index 00000000..ee827a99 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/solver.py @@ -0,0 +1,67 @@ +# solver.py + +from mesh import Mesh +from domain import Domain +from solution import Solution +from config import CfdConfig +from result import ResultAssembler +from problem import Problem +from initial_condition import InitialConditionFactory +from boundary import BoundaryConditionFactory +from residual import ResidualCalculator +from time_integration import TimeIntegrator,TimeIntegratorFactory + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, problem: Problem, mesh): + self.problem = problem + self.config = problem.config + self.domain = Domain(problem.config, mesh) + self.solution = Solution(problem.config, self.domain) + + self.initial_condition = InitialConditionFactory.create(self.config) + self.boundary_condition = BoundaryConditionFactory.create(self) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + + # ==================== 公共接口(供 TimeIntegrator 调用) ==================== + def compute_residual(self): + """计算物理残差(封装重建→通量→散度)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件""" + self.boundary_condition.apply(self.solution.u) + + # ==================== 初始化 ==================== + def initialize(self): + """初始化全场:先 IC,再 BC,最后同步 old field""" + self.initial_condition.apply(self.solution) + # 应用边界条件到初始场 + self.apply_boundary() + # 同步 old field + self.solution.update_old_field() + + + def run(self): + self.initialize() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + self.result = ResultAssembler.assemble(self) + return self.result["numerical"] + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01b/time_integration.py b/example/1d-linear-convection/weno3/python-v1/01b/time_integration.py new file mode 100644 index 00000000..6cdb3006 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01b/time_integration.py @@ -0,0 +1,124 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.cfd.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.apply_boundary() + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class TimeIntegratorFactory: + """时间推进器工厂""" + + @staticmethod + def create(cfd) -> 'TimeIntegrator': + """ + 创建时间推进器实例 + + Args: + cfd: CFD对象 + + Returns: + 时间推进器实例 + """ + from factories.base_factory import BaseFactory + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/boundary.py b/example/1d-linear-convection/weno3/python-v1/01c/boundary.py new file mode 100644 index 00000000..3a271aa2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/boundary.py @@ -0,0 +1,104 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + diff --git a/example/1d-linear-convection/weno3/python-v1/01c/cfd_registry.py b/example/1d-linear-convection/weno3/python-v1/01c/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/config.py b/example/1d-linear-convection/weno3/python-v1/01c/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/domain.py b/example/1d-linear-convection/weno3/python-v1/01c/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/factories/base_factory.py b/example/1d-linear-convection/weno3/python-v1/01c/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/flux/__init__.py b/example/1d-linear-convection/weno3/python-v1/01c/flux/__init__.py new file mode 100644 index 00000000..432a36cb --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/flux/__init__.py @@ -0,0 +1,7 @@ +# flux/__init__.py +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 确保子模块被导入以触发注册 +from . import rusanov +from . import engquist_osher \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/flux/base.py b/example/1d-linear-convection/weno3/python-v1/01c/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python-v1/01c/flux/engquist_osher.py new file mode 100644 index 00000000..34ad5f9c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/flux/engquist_osher.py @@ -0,0 +1,19 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from cfd_registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/flux/factory.py b/example/1d-linear-convection/weno3/python-v1/01c/flux/factory.py new file mode 100644 index 00000000..c2f6f075 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/flux/factory.py @@ -0,0 +1,25 @@ +# flux/factory.py +""" +通量计算器专用工厂(封装注册细节,提供清晰接口) +符合你希望“将创建逻辑封装在工厂中”的设计原则 +""" + +from factories.base_factory import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/flux/rusanov.py b/example/1d-linear-convection/weno3/python-v1/01c/flux/rusanov.py new file mode 100644 index 00000000..62dd207c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/flux/rusanov.py @@ -0,0 +1,21 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from cfd_registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/initial_condition.py b/example/1d-linear-convection/weno3/python-v1/01c/initial_condition.py new file mode 100644 index 00000000..963e9cdd --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/initial_condition.py @@ -0,0 +1,107 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def exact_solution(self, cfd): + """ + 默认解析解:线性对流 u(x, t) = u0(x - c * t) + 子类可重写以支持更复杂物理 + """ + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * t + L) % L + return self.evaluate_at(x_shifted) + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', config.ic_type, config) + + @classmethod + def get_exact_solution(cls, cfd): + return cls.create(cfd.config).exact_solution(cfd) # 一行! + diff --git a/example/1d-linear-convection/weno3/python-v1/01c/mesh.py b/example/1d-linear-convection/weno3/python-v1/01c/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/plotter.py b/example/1d-linear-convection/weno3/python-v1/01c/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/problem.py b/example/1d-linear-convection/weno3/python-v1/01c/problem.py new file mode 100644 index 00000000..53719f26 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/problem.py @@ -0,0 +1,38 @@ +# problem.py +""" +问题定义模块:每个 Problem 子类代表一个完整测试用例 +包含初始条件、解析解(可选)、物理方程等 +""" + +from abc import ABC, abstractmethod +from initial_condition import InitialConditionFactory + + +class Problem(ABC): + """ + 抽象问题基类 + 每个具体问题(如线性对流、Sod 激波管)应继承此类 + """ + def __init__(self, config): + self.config = config + + @abstractmethod + def initial_condition(self): + """ + 返回 InitialCondition 实例 + """ + pass + + def exact_solution(self, cfd): + """ + 可选:返回解析解(数值数组) + 若无解析解,可抛出 NotImplementedError 或返回 None + """ + x = cfd.domain.mesh.xcc + raise NotImplementedError( + f"Problem '{self.__class__.__name__}' does not provide an exact solution." + ) + + @property + def name(self): + return self.__class__.__name__ \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/problems/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/01c/problems/linear_advection.py new file mode 100644 index 00000000..ea1e2df2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/problems/linear_advection.py @@ -0,0 +1,27 @@ +# problems/linear_advection.py +""" +线性对流问题:支持任意初始条件 + 周期平移解析解 +""" + +import numpy as np +from problem import Problem +from initial_condition import InitialConditionFactory + + +class LinearAdvectionProblem(Problem): + """ + 线性对流问题 u_t + c u_x = 0 + 解析解:u(x, t) = u0(x - c * t) + """ + + def initial_condition(self): + return InitialConditionFactory.create(self.config) + + def exact_solution(self, cfd): + ic = self.initial_condition() + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + x_shifted = (x - c * t + L) % L + return ic.evaluate_at(x_shifted) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python-v1/01c/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/reconstructor/base.py b/example/1d-linear-convection/weno3/python-v1/01c/reconstructor/base.py new file mode 100644 index 00000000..094b4712 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def compute_face_values(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/reconstructor/eno.py b/example/1d-linear-convection/weno3/python-v1/01c/reconstructor/eno.py new file mode 100644 index 00000000..8fde4333 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def compute_face_values(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/reconstructor/factory.py b/example/1d-linear-convection/weno3/python-v1/01c/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python-v1/01c/reconstructor/weno3.py new file mode 100644 index 00000000..929061c1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/reconstructor/weno3.py @@ -0,0 +1,59 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python-v1/01c/registry.py b/example/1d-linear-convection/weno3/python-v1/01c/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/residual.py b/example/1d-linear-convection/weno3/python-v1/01c/residual.py new file mode 100644 index 00000000..e0f96cd3 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux.factory import FluxCalculatorFactory +from reconstructor import ReconstructorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + + self.reconstructor = ReconstructorFactory.create(self.config, self.domain) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._compute_face_values() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _compute_face_values(self): + """私有方法:界面值重建""" + self.reconstructor.compute_face_values(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/result.py b/example/1d-linear-convection/weno3/python-v1/01c/result.py new file mode 100644 index 00000000..acd257b0 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/result.py @@ -0,0 +1,21 @@ +# result.py + +from initial_condition import InitialConditionFactory + +class ResultAssembler: + @staticmethod + def assemble(cfd: 'Cfd') -> dict: + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied].copy() + analytical = InitialConditionFactory.get_exact_solution(cfd) + + return { + "x": cfd.domain.mesh.xcc, + "numerical": u_numerical, + "analytical": analytical, + "config": { + "scheme": cfd.config.recon_scheme, + "order": cfd.config.spatial_order, + "rk_order": cfd.config.rk_order, + "final_time": cfd.config.final_time + } + } \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/run_eno_weno.py b/example/1d-linear-convection/weno3/python-v1/01c/run_eno_weno.py new file mode 100644 index 00000000..25efa62f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/run_eno_weno.py @@ -0,0 +1,52 @@ +# run_eno_weno.py + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +# ✅ 新增:导入 Problem +from problems.linear_advection import LinearAdvectionProblem + + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行 ENO3 求解 + print("Running ENO3 solver...") + config_eno3 = CfdConfig() + config_eno3.with_reconstruction("eno", 3) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno3 = LinearAdvectionProblem(config_eno3) + cfd_eno3 = Cfd(problem_eno3, mesh) # ← 注入 problem + cfd_eno3.run() + + # 3. 配置并运行 WENO3 求解 + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno3 = LinearAdvectionProblem(config_weno3) + cfd_weno3 = Cfd(problem_weno3, mesh) # ← 注入 problem + cfd_weno3.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" + ) + + +if __name__ == "__main__": + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/solution.py b/example/1d-linear-convection/weno3/python-v1/01c/solution.py new file mode 100644 index 00000000..90666c34 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/solution.py @@ -0,0 +1,32 @@ +# solution.py +import numpy as np + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/solver.py b/example/1d-linear-convection/weno3/python-v1/01c/solver.py new file mode 100644 index 00000000..ee827a99 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/solver.py @@ -0,0 +1,67 @@ +# solver.py + +from mesh import Mesh +from domain import Domain +from solution import Solution +from config import CfdConfig +from result import ResultAssembler +from problem import Problem +from initial_condition import InitialConditionFactory +from boundary import BoundaryConditionFactory +from residual import ResidualCalculator +from time_integration import TimeIntegrator,TimeIntegratorFactory + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, problem: Problem, mesh): + self.problem = problem + self.config = problem.config + self.domain = Domain(problem.config, mesh) + self.solution = Solution(problem.config, self.domain) + + self.initial_condition = InitialConditionFactory.create(self.config) + self.boundary_condition = BoundaryConditionFactory.create(self) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + + # ==================== 公共接口(供 TimeIntegrator 调用) ==================== + def compute_residual(self): + """计算物理残差(封装重建→通量→散度)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件""" + self.boundary_condition.apply(self.solution.u) + + # ==================== 初始化 ==================== + def initialize(self): + """初始化全场:先 IC,再 BC,最后同步 old field""" + self.initial_condition.apply(self.solution) + # 应用边界条件到初始场 + self.apply_boundary() + # 同步 old field + self.solution.update_old_field() + + + def run(self): + self.initialize() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + self.result = ResultAssembler.assemble(self) + return self.result["numerical"] + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/time_integration/__init__.py b/example/1d-linear-convection/weno3/python-v1/01c/time_integration/__init__.py new file mode 100644 index 00000000..6342de3b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/time_integration/__init__.py @@ -0,0 +1,8 @@ +# time_integration/__init__.py + +# 导出统一接口 +from .factory import TimeIntegratorFactory +from .base import TimeIntegrator + +# 触发子模块注册(关键!) +from . import rk1, rk2, rk3 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/time_integration/base.py b/example/1d-linear-convection/weno3/python-v1/01c/time_integration/base.py new file mode 100644 index 00000000..0cdf29a4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/time_integration/base.py @@ -0,0 +1,27 @@ +# time_integration/base.py + +from abc import ABC, abstractmethod + +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + + @abstractmethod + def step(self, dt): + pass + + def compute_residual(self): + """计算残差(委托给 Cfd)""" + self.cfd.compute_residual() + + def apply_boundary(self): + """应用边界条件(委托给 Cfd)""" + self.cfd.apply_boundary() + + def map_idx(self, i): + """物理网格索引 → 残差数组索引""" + return i - self.domain.ist \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/time_integration/factory.py b/example/1d-linear-convection/weno3/python-v1/01c/time_integration/factory.py new file mode 100644 index 00000000..b9dd518f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/time_integration/factory.py @@ -0,0 +1,10 @@ +# time_integration/factory.py + +from factories.base_factory import BaseFactory + +class TimeIntegratorFactory: + @staticmethod + def create(cfd) -> 'TimeIntegrator': + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/time_integration/rk1.py b/example/1d-linear-convection/weno3/python-v1/01c/time_integration/rk1.py new file mode 100644 index 00000000..5906fe57 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/time_integration/rk1.py @@ -0,0 +1,15 @@ +# time_integration/rk1.py + +from .base import TimeIntegrator +from cfd_registry import register_component + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/time_integration/rk2.py b/example/1d-linear-convection/weno3/python-v1/01c/time_integration/rk2.py new file mode 100644 index 00000000..311c4543 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/time_integration/rk2.py @@ -0,0 +1,29 @@ +# time_integration/rk2.py + +from .base import TimeIntegrator +from cfd_registry import register_component + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + 0.5 * self.solution.un[i] + + 0.5 * self.solution.u[i] + + 0.5 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01c/time_integration/rk3.py b/example/1d-linear-convection/weno3/python-v1/01c/time_integration/rk3.py new file mode 100644 index 00000000..e91cd68c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01c/time_integration/rk3.py @@ -0,0 +1,43 @@ +# time_integration/rk3.py + +from .base import TimeIntegrator +from cfd_registry import register_component + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # Stage 1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # Stage 2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = ( + 0.75 * self.solution.un[i] + + 0.25 * self.solution.u[i] + + 0.25 * dt * self.solution.res[j] + ) + self.solution.u[:] = u2 + self.apply_boundary() + + # Stage 3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + c1 * self.solution.un[i] + + c2 * self.solution.u[i] + + c3 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/boundary.py b/example/1d-linear-convection/weno3/python-v1/01d/boundary.py new file mode 100644 index 00000000..2d2cfbaf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/boundary.py @@ -0,0 +1,104 @@ +from abc import ABC, abstractmethod +from core.registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from core.registry import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + diff --git a/example/1d-linear-convection/weno3/python-v1/01d/config.py b/example/1d-linear-convection/weno3/python-v1/01d/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/core/registry.py b/example/1d-linear-convection/weno3/python-v1/01d/core/registry.py new file mode 100644 index 00000000..a6038a50 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/core/registry.py @@ -0,0 +1,79 @@ +# core/registry.py +""" +统一注册系统 + 通用工厂 +- ComponentRegistry: 组件注册表 +- register_component: 装饰器 +- BaseFactory: 通用工厂类 +""" + +from typing import Dict, Type, Any + + +# ==================== 1. 注册表核心 ==================== +class ComponentRegistry: + """组件注册表""" + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True + + @classmethod + def set_verbose(cls, verbose: bool): + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + if category not in cls._registries: + cls._registries[category] = {} + if name in cls._registries[category]: + if cls._registries[category][name] != component_class and cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + if category not in cls._registries: + raise ValueError(f"❌ 未知类别: {category} (可用: {list(cls._registries.keys())})") + if name not in cls._registries[category]: + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {list(cls._registries[category].keys())})") + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) for cat, comps in cls._registries.items()} + + +# ==================== 2. 装饰器 ==================== +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + +# ==================== 3. 通用工厂 ==================== +class BaseFactory: + """通用工厂基类""" + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + name_lower = name.lower() + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + available = ComponentRegistry.list_all().get(category, []) + if available: + error_msg = f"不支持的 {category} 类型 '{name}'。可用类型:{available}" + else: + error_msg = f"不支持的 {category} 类型 '{name}'(无已注册组件)" + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/domain.py b/example/1d-linear-convection/weno3/python-v1/01d/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/flux/__init__.py b/example/1d-linear-convection/weno3/python-v1/01d/flux/__init__.py new file mode 100644 index 00000000..432a36cb --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/flux/__init__.py @@ -0,0 +1,7 @@ +# flux/__init__.py +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 确保子模块被导入以触发注册 +from . import rusanov +from . import engquist_osher \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/flux/base.py b/example/1d-linear-convection/weno3/python-v1/01d/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python-v1/01d/flux/engquist_osher.py new file mode 100644 index 00000000..b65e9d7a --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/flux/engquist_osher.py @@ -0,0 +1,19 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/flux/factory.py b/example/1d-linear-convection/weno3/python-v1/01d/flux/factory.py new file mode 100644 index 00000000..5ae77c3f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/flux/factory.py @@ -0,0 +1,25 @@ +# flux/factory.py +""" +通量计算器专用工厂(封装注册细节,提供清晰接口) +符合你希望“将创建逻辑封装在工厂中”的设计原则 +""" + +from core.registry import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/flux/rusanov.py b/example/1d-linear-convection/weno3/python-v1/01d/flux/rusanov.py new file mode 100644 index 00000000..fdc3c33d --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/flux/rusanov.py @@ -0,0 +1,21 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/initial_condition.py b/example/1d-linear-convection/weno3/python-v1/01d/initial_condition.py new file mode 100644 index 00000000..08873719 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/initial_condition.py @@ -0,0 +1,107 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from core.registry import ComponentRegistry, register_component + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def exact_solution(self, cfd): + """ + 默认解析解:线性对流 u(x, t) = u0(x - c * t) + 子类可重写以支持更复杂物理 + """ + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * t + L) % L + return self.evaluate_at(x_shifted) + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from core.registry import BaseFactory + return BaseFactory.create_component('initial_condition', config.ic_type, config) + + @classmethod + def get_exact_solution(cls, cfd): + return cls.create(cfd.config).exact_solution(cfd) + diff --git a/example/1d-linear-convection/weno3/python-v1/01d/mesh.py b/example/1d-linear-convection/weno3/python-v1/01d/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/plotter.py b/example/1d-linear-convection/weno3/python-v1/01d/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/problem.py b/example/1d-linear-convection/weno3/python-v1/01d/problem.py new file mode 100644 index 00000000..53719f26 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/problem.py @@ -0,0 +1,38 @@ +# problem.py +""" +问题定义模块:每个 Problem 子类代表一个完整测试用例 +包含初始条件、解析解(可选)、物理方程等 +""" + +from abc import ABC, abstractmethod +from initial_condition import InitialConditionFactory + + +class Problem(ABC): + """ + 抽象问题基类 + 每个具体问题(如线性对流、Sod 激波管)应继承此类 + """ + def __init__(self, config): + self.config = config + + @abstractmethod + def initial_condition(self): + """ + 返回 InitialCondition 实例 + """ + pass + + def exact_solution(self, cfd): + """ + 可选:返回解析解(数值数组) + 若无解析解,可抛出 NotImplementedError 或返回 None + """ + x = cfd.domain.mesh.xcc + raise NotImplementedError( + f"Problem '{self.__class__.__name__}' does not provide an exact solution." + ) + + @property + def name(self): + return self.__class__.__name__ \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/problems/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/01d/problems/linear_advection.py new file mode 100644 index 00000000..ea1e2df2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/problems/linear_advection.py @@ -0,0 +1,27 @@ +# problems/linear_advection.py +""" +线性对流问题:支持任意初始条件 + 周期平移解析解 +""" + +import numpy as np +from problem import Problem +from initial_condition import InitialConditionFactory + + +class LinearAdvectionProblem(Problem): + """ + 线性对流问题 u_t + c u_x = 0 + 解析解:u(x, t) = u0(x - c * t) + """ + + def initial_condition(self): + return InitialConditionFactory.create(self.config) + + def exact_solution(self, cfd): + ic = self.initial_condition() + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + x_shifted = (x - c * t + L) % L + return ic.evaluate_at(x_shifted) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python-v1/01d/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/reconstructor/base.py b/example/1d-linear-convection/weno3/python-v1/01d/reconstructor/base.py new file mode 100644 index 00000000..094b4712 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def compute_face_values(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/reconstructor/eno.py b/example/1d-linear-convection/weno3/python-v1/01d/reconstructor/eno.py new file mode 100644 index 00000000..4c8fc1e4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def compute_face_values(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/reconstructor/factory.py b/example/1d-linear-convection/weno3/python-v1/01d/reconstructor/factory.py new file mode 100644 index 00000000..d0e60a79 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from core.registry import ComponentRegistry, register_component + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from core.registry import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python-v1/01d/reconstructor/weno3.py new file mode 100644 index 00000000..cc7d82df --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/reconstructor/weno3.py @@ -0,0 +1,59 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python-v1/01d/residual.py b/example/1d-linear-convection/weno3/python-v1/01d/residual.py new file mode 100644 index 00000000..e0f96cd3 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux.factory import FluxCalculatorFactory +from reconstructor import ReconstructorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + + self.reconstructor = ReconstructorFactory.create(self.config, self.domain) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._compute_face_values() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _compute_face_values(self): + """私有方法:界面值重建""" + self.reconstructor.compute_face_values(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/result.py b/example/1d-linear-convection/weno3/python-v1/01d/result.py new file mode 100644 index 00000000..acd257b0 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/result.py @@ -0,0 +1,21 @@ +# result.py + +from initial_condition import InitialConditionFactory + +class ResultAssembler: + @staticmethod + def assemble(cfd: 'Cfd') -> dict: + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied].copy() + analytical = InitialConditionFactory.get_exact_solution(cfd) + + return { + "x": cfd.domain.mesh.xcc, + "numerical": u_numerical, + "analytical": analytical, + "config": { + "scheme": cfd.config.recon_scheme, + "order": cfd.config.spatial_order, + "rk_order": cfd.config.rk_order, + "final_time": cfd.config.final_time + } + } \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/run_eno_weno.py b/example/1d-linear-convection/weno3/python-v1/01d/run_eno_weno.py new file mode 100644 index 00000000..25efa62f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/run_eno_weno.py @@ -0,0 +1,52 @@ +# run_eno_weno.py + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +# ✅ 新增:导入 Problem +from problems.linear_advection import LinearAdvectionProblem + + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行 ENO3 求解 + print("Running ENO3 solver...") + config_eno3 = CfdConfig() + config_eno3.with_reconstruction("eno", 3) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno3 = LinearAdvectionProblem(config_eno3) + cfd_eno3 = Cfd(problem_eno3, mesh) # ← 注入 problem + cfd_eno3.run() + + # 3. 配置并运行 WENO3 求解 + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno3 = LinearAdvectionProblem(config_weno3) + cfd_weno3 = Cfd(problem_weno3, mesh) # ← 注入 problem + cfd_weno3.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" + ) + + +if __name__ == "__main__": + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/solution.py b/example/1d-linear-convection/weno3/python-v1/01d/solution.py new file mode 100644 index 00000000..90666c34 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/solution.py @@ -0,0 +1,32 @@ +# solution.py +import numpy as np + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/solver.py b/example/1d-linear-convection/weno3/python-v1/01d/solver.py new file mode 100644 index 00000000..ee827a99 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/solver.py @@ -0,0 +1,67 @@ +# solver.py + +from mesh import Mesh +from domain import Domain +from solution import Solution +from config import CfdConfig +from result import ResultAssembler +from problem import Problem +from initial_condition import InitialConditionFactory +from boundary import BoundaryConditionFactory +from residual import ResidualCalculator +from time_integration import TimeIntegrator,TimeIntegratorFactory + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, problem: Problem, mesh): + self.problem = problem + self.config = problem.config + self.domain = Domain(problem.config, mesh) + self.solution = Solution(problem.config, self.domain) + + self.initial_condition = InitialConditionFactory.create(self.config) + self.boundary_condition = BoundaryConditionFactory.create(self) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + + # ==================== 公共接口(供 TimeIntegrator 调用) ==================== + def compute_residual(self): + """计算物理残差(封装重建→通量→散度)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件""" + self.boundary_condition.apply(self.solution.u) + + # ==================== 初始化 ==================== + def initialize(self): + """初始化全场:先 IC,再 BC,最后同步 old field""" + self.initial_condition.apply(self.solution) + # 应用边界条件到初始场 + self.apply_boundary() + # 同步 old field + self.solution.update_old_field() + + + def run(self): + self.initialize() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + self.result = ResultAssembler.assemble(self) + return self.result["numerical"] + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/time_integration/__init__.py b/example/1d-linear-convection/weno3/python-v1/01d/time_integration/__init__.py new file mode 100644 index 00000000..6342de3b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/time_integration/__init__.py @@ -0,0 +1,8 @@ +# time_integration/__init__.py + +# 导出统一接口 +from .factory import TimeIntegratorFactory +from .base import TimeIntegrator + +# 触发子模块注册(关键!) +from . import rk1, rk2, rk3 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/time_integration/base.py b/example/1d-linear-convection/weno3/python-v1/01d/time_integration/base.py new file mode 100644 index 00000000..0cdf29a4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/time_integration/base.py @@ -0,0 +1,27 @@ +# time_integration/base.py + +from abc import ABC, abstractmethod + +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + + @abstractmethod + def step(self, dt): + pass + + def compute_residual(self): + """计算残差(委托给 Cfd)""" + self.cfd.compute_residual() + + def apply_boundary(self): + """应用边界条件(委托给 Cfd)""" + self.cfd.apply_boundary() + + def map_idx(self, i): + """物理网格索引 → 残差数组索引""" + return i - self.domain.ist \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/time_integration/factory.py b/example/1d-linear-convection/weno3/python-v1/01d/time_integration/factory.py new file mode 100644 index 00000000..94662db2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/time_integration/factory.py @@ -0,0 +1,10 @@ +# time_integration/factory.py + +from core.registry import BaseFactory + +class TimeIntegratorFactory: + @staticmethod + def create(cfd) -> 'TimeIntegrator': + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/time_integration/rk1.py b/example/1d-linear-convection/weno3/python-v1/01d/time_integration/rk1.py new file mode 100644 index 00000000..b4c8a021 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/time_integration/rk1.py @@ -0,0 +1,15 @@ +# time_integration/rk1.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/time_integration/rk2.py b/example/1d-linear-convection/weno3/python-v1/01d/time_integration/rk2.py new file mode 100644 index 00000000..6d2be304 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/time_integration/rk2.py @@ -0,0 +1,29 @@ +# time_integration/rk2.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + 0.5 * self.solution.un[i] + + 0.5 * self.solution.u[i] + + 0.5 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01d/time_integration/rk3.py b/example/1d-linear-convection/weno3/python-v1/01d/time_integration/rk3.py new file mode 100644 index 00000000..c70791e1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01d/time_integration/rk3.py @@ -0,0 +1,43 @@ +# time_integration/rk3.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # Stage 1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # Stage 2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = ( + 0.75 * self.solution.un[i] + + 0.25 * self.solution.u[i] + + 0.25 * dt * self.solution.res[j] + ) + self.solution.u[:] = u2 + self.apply_boundary() + + # Stage 3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + c1 * self.solution.un[i] + + c2 * self.solution.u[i] + + c3 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/boundary.py b/example/1d-linear-convection/weno3/python-v1/01e/boundary.py new file mode 100644 index 00000000..2d2cfbaf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/boundary.py @@ -0,0 +1,104 @@ +from abc import ABC, abstractmethod +from core.registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from core.registry import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + diff --git a/example/1d-linear-convection/weno3/python-v1/01e/config.py b/example/1d-linear-convection/weno3/python-v1/01e/config.py new file mode 100644 index 00000000..63d05faa --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/config.py @@ -0,0 +1,42 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + #self.ic_type = "sin" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/core/registry.py b/example/1d-linear-convection/weno3/python-v1/01e/core/registry.py new file mode 100644 index 00000000..a6038a50 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/core/registry.py @@ -0,0 +1,79 @@ +# core/registry.py +""" +统一注册系统 + 通用工厂 +- ComponentRegistry: 组件注册表 +- register_component: 装饰器 +- BaseFactory: 通用工厂类 +""" + +from typing import Dict, Type, Any + + +# ==================== 1. 注册表核心 ==================== +class ComponentRegistry: + """组件注册表""" + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True + + @classmethod + def set_verbose(cls, verbose: bool): + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + if category not in cls._registries: + cls._registries[category] = {} + if name in cls._registries[category]: + if cls._registries[category][name] != component_class and cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + if category not in cls._registries: + raise ValueError(f"❌ 未知类别: {category} (可用: {list(cls._registries.keys())})") + if name not in cls._registries[category]: + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {list(cls._registries[category].keys())})") + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) for cat, comps in cls._registries.items()} + + +# ==================== 2. 装饰器 ==================== +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + +# ==================== 3. 通用工厂 ==================== +class BaseFactory: + """通用工厂基类""" + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + name_lower = name.lower() + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + available = ComponentRegistry.list_all().get(category, []) + if available: + error_msg = f"不支持的 {category} 类型 '{name}'。可用类型:{available}" + else: + error_msg = f"不支持的 {category} 类型 '{name}'(无已注册组件)" + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/domain.py b/example/1d-linear-convection/weno3/python-v1/01e/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/flux/__init__.py b/example/1d-linear-convection/weno3/python-v1/01e/flux/__init__.py new file mode 100644 index 00000000..432a36cb --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/flux/__init__.py @@ -0,0 +1,7 @@ +# flux/__init__.py +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 确保子模块被导入以触发注册 +from . import rusanov +from . import engquist_osher \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/flux/base.py b/example/1d-linear-convection/weno3/python-v1/01e/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python-v1/01e/flux/engquist_osher.py new file mode 100644 index 00000000..b65e9d7a --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/flux/engquist_osher.py @@ -0,0 +1,19 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/flux/factory.py b/example/1d-linear-convection/weno3/python-v1/01e/flux/factory.py new file mode 100644 index 00000000..5ae77c3f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/flux/factory.py @@ -0,0 +1,25 @@ +# flux/factory.py +""" +通量计算器专用工厂(封装注册细节,提供清晰接口) +符合你希望“将创建逻辑封装在工厂中”的设计原则 +""" + +from core.registry import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/flux/rusanov.py b/example/1d-linear-convection/weno3/python-v1/01e/flux/rusanov.py new file mode 100644 index 00000000..fdc3c33d --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/flux/rusanov.py @@ -0,0 +1,21 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/initial_condition.py b/example/1d-linear-convection/weno3/python-v1/01e/initial_condition.py new file mode 100644 index 00000000..dda8a3bd --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/initial_condition.py @@ -0,0 +1,90 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from core.registry import ComponentRegistry, register_component + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from core.registry import BaseFactory + return BaseFactory.create_component('initial_condition', config.ic_type, config) + diff --git a/example/1d-linear-convection/weno3/python-v1/01e/mesh.py b/example/1d-linear-convection/weno3/python-v1/01e/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/plotter.py b/example/1d-linear-convection/weno3/python-v1/01e/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/problem.py b/example/1d-linear-convection/weno3/python-v1/01e/problem.py new file mode 100644 index 00000000..53719f26 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/problem.py @@ -0,0 +1,38 @@ +# problem.py +""" +问题定义模块:每个 Problem 子类代表一个完整测试用例 +包含初始条件、解析解(可选)、物理方程等 +""" + +from abc import ABC, abstractmethod +from initial_condition import InitialConditionFactory + + +class Problem(ABC): + """ + 抽象问题基类 + 每个具体问题(如线性对流、Sod 激波管)应继承此类 + """ + def __init__(self, config): + self.config = config + + @abstractmethod + def initial_condition(self): + """ + 返回 InitialCondition 实例 + """ + pass + + def exact_solution(self, cfd): + """ + 可选:返回解析解(数值数组) + 若无解析解,可抛出 NotImplementedError 或返回 None + """ + x = cfd.domain.mesh.xcc + raise NotImplementedError( + f"Problem '{self.__class__.__name__}' does not provide an exact solution." + ) + + @property + def name(self): + return self.__class__.__name__ \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/problems/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/01e/problems/linear_advection.py new file mode 100644 index 00000000..a9936df7 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/problems/linear_advection.py @@ -0,0 +1,28 @@ +# problems/linear_advection.py +""" +线性对流问题:支持任意初始条件 + 周期平移解析解 +""" + +import numpy as np +from problem import Problem +from initial_condition import InitialConditionFactory + + +class LinearAdvectionProblem(Problem): + """ + 线性对流问题 u_t + c u_x = 0 + 解析解:u(x, t) = u0(x - c * t) + """ + + def initial_condition(self): + return InitialConditionFactory.create(self.config) + + def exact_solution(self, cfd): + ic = self.initial_condition() + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + x_shifted = (x - c * t + L) % L + return ic.evaluate_at(x_shifted) + diff --git a/example/1d-linear-convection/weno3/python-v1/01e/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python-v1/01e/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/reconstructor/base.py b/example/1d-linear-convection/weno3/python-v1/01e/reconstructor/base.py new file mode 100644 index 00000000..094b4712 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def compute_face_values(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/reconstructor/eno.py b/example/1d-linear-convection/weno3/python-v1/01e/reconstructor/eno.py new file mode 100644 index 00000000..4c8fc1e4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def compute_face_values(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/reconstructor/factory.py b/example/1d-linear-convection/weno3/python-v1/01e/reconstructor/factory.py new file mode 100644 index 00000000..d0e60a79 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from core.registry import ComponentRegistry, register_component + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from core.registry import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python-v1/01e/reconstructor/weno3.py new file mode 100644 index 00000000..cc7d82df --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/reconstructor/weno3.py @@ -0,0 +1,59 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python-v1/01e/residual.py b/example/1d-linear-convection/weno3/python-v1/01e/residual.py new file mode 100644 index 00000000..e0f96cd3 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux.factory import FluxCalculatorFactory +from reconstructor import ReconstructorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + + self.reconstructor = ReconstructorFactory.create(self.config, self.domain) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._compute_face_values() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _compute_face_values(self): + """私有方法:界面值重建""" + self.reconstructor.compute_face_values(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/result.py b/example/1d-linear-convection/weno3/python-v1/01e/result.py new file mode 100644 index 00000000..5e572216 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/result.py @@ -0,0 +1,25 @@ +# result.py + +class ResultAssembler: + @staticmethod + def assemble(cfd: 'Cfd') -> dict: + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied].copy() + + # ✅ 从 problem 获取解析解(唯一正确路径) + try: + analytical = cfd.problem.exact_solution(cfd) + except NotImplementedError: + analytical = None # 或 np.full_like(u_numerical, np.nan) + + return { + "x": cfd.domain.mesh.xcc, + "numerical": u_numerical, + "analytical": analytical, + "config": { + "scheme": cfd.config.recon_scheme, + "order": cfd.config.spatial_order, + "rk_order": cfd.config.rk_order, + "final_time": cfd.config.final_time, + "problem": cfd.problem.name, + } + } \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/run_eno_weno.py b/example/1d-linear-convection/weno3/python-v1/01e/run_eno_weno.py new file mode 100644 index 00000000..25efa62f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/run_eno_weno.py @@ -0,0 +1,52 @@ +# run_eno_weno.py + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +# ✅ 新增:导入 Problem +from problems.linear_advection import LinearAdvectionProblem + + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行 ENO3 求解 + print("Running ENO3 solver...") + config_eno3 = CfdConfig() + config_eno3.with_reconstruction("eno", 3) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno3 = LinearAdvectionProblem(config_eno3) + cfd_eno3 = Cfd(problem_eno3, mesh) # ← 注入 problem + cfd_eno3.run() + + # 3. 配置并运行 WENO3 求解 + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno3 = LinearAdvectionProblem(config_weno3) + cfd_weno3 = Cfd(problem_weno3, mesh) # ← 注入 problem + cfd_weno3.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" + ) + + +if __name__ == "__main__": + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/solution.py b/example/1d-linear-convection/weno3/python-v1/01e/solution.py new file mode 100644 index 00000000..90666c34 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/solution.py @@ -0,0 +1,32 @@ +# solution.py +import numpy as np + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/solver.py b/example/1d-linear-convection/weno3/python-v1/01e/solver.py new file mode 100644 index 00000000..ee827a99 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/solver.py @@ -0,0 +1,67 @@ +# solver.py + +from mesh import Mesh +from domain import Domain +from solution import Solution +from config import CfdConfig +from result import ResultAssembler +from problem import Problem +from initial_condition import InitialConditionFactory +from boundary import BoundaryConditionFactory +from residual import ResidualCalculator +from time_integration import TimeIntegrator,TimeIntegratorFactory + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, problem: Problem, mesh): + self.problem = problem + self.config = problem.config + self.domain = Domain(problem.config, mesh) + self.solution = Solution(problem.config, self.domain) + + self.initial_condition = InitialConditionFactory.create(self.config) + self.boundary_condition = BoundaryConditionFactory.create(self) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + + # ==================== 公共接口(供 TimeIntegrator 调用) ==================== + def compute_residual(self): + """计算物理残差(封装重建→通量→散度)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件""" + self.boundary_condition.apply(self.solution.u) + + # ==================== 初始化 ==================== + def initialize(self): + """初始化全场:先 IC,再 BC,最后同步 old field""" + self.initial_condition.apply(self.solution) + # 应用边界条件到初始场 + self.apply_boundary() + # 同步 old field + self.solution.update_old_field() + + + def run(self): + self.initialize() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + self.result = ResultAssembler.assemble(self) + return self.result["numerical"] + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/time_integration/__init__.py b/example/1d-linear-convection/weno3/python-v1/01e/time_integration/__init__.py new file mode 100644 index 00000000..6342de3b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/time_integration/__init__.py @@ -0,0 +1,8 @@ +# time_integration/__init__.py + +# 导出统一接口 +from .factory import TimeIntegratorFactory +from .base import TimeIntegrator + +# 触发子模块注册(关键!) +from . import rk1, rk2, rk3 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/time_integration/base.py b/example/1d-linear-convection/weno3/python-v1/01e/time_integration/base.py new file mode 100644 index 00000000..0cdf29a4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/time_integration/base.py @@ -0,0 +1,27 @@ +# time_integration/base.py + +from abc import ABC, abstractmethod + +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + + @abstractmethod + def step(self, dt): + pass + + def compute_residual(self): + """计算残差(委托给 Cfd)""" + self.cfd.compute_residual() + + def apply_boundary(self): + """应用边界条件(委托给 Cfd)""" + self.cfd.apply_boundary() + + def map_idx(self, i): + """物理网格索引 → 残差数组索引""" + return i - self.domain.ist \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/time_integration/factory.py b/example/1d-linear-convection/weno3/python-v1/01e/time_integration/factory.py new file mode 100644 index 00000000..94662db2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/time_integration/factory.py @@ -0,0 +1,10 @@ +# time_integration/factory.py + +from core.registry import BaseFactory + +class TimeIntegratorFactory: + @staticmethod + def create(cfd) -> 'TimeIntegrator': + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/time_integration/rk1.py b/example/1d-linear-convection/weno3/python-v1/01e/time_integration/rk1.py new file mode 100644 index 00000000..b4c8a021 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/time_integration/rk1.py @@ -0,0 +1,15 @@ +# time_integration/rk1.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/time_integration/rk2.py b/example/1d-linear-convection/weno3/python-v1/01e/time_integration/rk2.py new file mode 100644 index 00000000..6d2be304 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/time_integration/rk2.py @@ -0,0 +1,29 @@ +# time_integration/rk2.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + 0.5 * self.solution.un[i] + + 0.5 * self.solution.u[i] + + 0.5 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01e/time_integration/rk3.py b/example/1d-linear-convection/weno3/python-v1/01e/time_integration/rk3.py new file mode 100644 index 00000000..c70791e1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01e/time_integration/rk3.py @@ -0,0 +1,43 @@ +# time_integration/rk3.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # Stage 1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # Stage 2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = ( + 0.75 * self.solution.un[i] + + 0.25 * self.solution.u[i] + + 0.25 * dt * self.solution.res[j] + ) + self.solution.u[:] = u2 + self.apply_boundary() + + # Stage 3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + c1 * self.solution.un[i] + + c2 * self.solution.u[i] + + c3 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/boundary.py b/example/1d-linear-convection/weno3/python-v1/01f/boundary.py new file mode 100644 index 00000000..2d2cfbaf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/boundary.py @@ -0,0 +1,104 @@ +from abc import ABC, abstractmethod +from core.registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from core.registry import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + diff --git a/example/1d-linear-convection/weno3/python-v1/01f/config.py b/example/1d-linear-convection/weno3/python-v1/01f/config.py new file mode 100644 index 00000000..63d05faa --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/config.py @@ -0,0 +1,42 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + #self.ic_type = "sin" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/core/registry.py b/example/1d-linear-convection/weno3/python-v1/01f/core/registry.py new file mode 100644 index 00000000..a6038a50 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/core/registry.py @@ -0,0 +1,79 @@ +# core/registry.py +""" +统一注册系统 + 通用工厂 +- ComponentRegistry: 组件注册表 +- register_component: 装饰器 +- BaseFactory: 通用工厂类 +""" + +from typing import Dict, Type, Any + + +# ==================== 1. 注册表核心 ==================== +class ComponentRegistry: + """组件注册表""" + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True + + @classmethod + def set_verbose(cls, verbose: bool): + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + if category not in cls._registries: + cls._registries[category] = {} + if name in cls._registries[category]: + if cls._registries[category][name] != component_class and cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + if category not in cls._registries: + raise ValueError(f"❌ 未知类别: {category} (可用: {list(cls._registries.keys())})") + if name not in cls._registries[category]: + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {list(cls._registries[category].keys())})") + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) for cat, comps in cls._registries.items()} + + +# ==================== 2. 装饰器 ==================== +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + +# ==================== 3. 通用工厂 ==================== +class BaseFactory: + """通用工厂基类""" + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + name_lower = name.lower() + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + available = ComponentRegistry.list_all().get(category, []) + if available: + error_msg = f"不支持的 {category} 类型 '{name}'。可用类型:{available}" + else: + error_msg = f"不支持的 {category} 类型 '{name}'(无已注册组件)" + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/domain.py b/example/1d-linear-convection/weno3/python-v1/01f/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/equations/base.py b/example/1d-linear-convection/weno3/python-v1/01f/equations/base.py new file mode 100644 index 00000000..2e5c8776 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/equations/base.py @@ -0,0 +1,47 @@ +# equations/base.py + +from abc import ABC, abstractmethod +import numpy as np + +class Equation(ABC): + """ + 控制方程抽象基类 + 所有物理方程(线性对流、Euler、MHD)必须继承此类 + """ + + @property + @abstractmethod + def num_equations(self) -> int: + """返回方程组变量数(标量=1,Euler=3,MHD=8)""" + pass + + @abstractmethod + def flux(self, u): + """ + 计算通量函数 f(u) + :param u: 状态向量 (num_equations,) + :return: 通量向量 (num_equations,) + """ + pass + + @abstractmethod + def max_wave_speed(self, u): + """ + 计算最大波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + def exact_solution(self, x, t, initial_condition_func): + """ + 可选:提供解析解 + 子类可重写;若无解析解,调用方应捕获 NotImplementedError + :param x: 空间坐标数组 (ncells,) + :param t: 时间 + :param initial_condition_func: 初值函数 u0(x) -> (num_equations, ncells) + :return: 解 u(x,t) -> (num_equations, ncells) + """ + raise NotImplementedError( + f"Equation '{self.__class__.__name__}' does not provide an exact solution." + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/equations/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/01f/equations/linear_advection.py new file mode 100644 index 00000000..1768a0f0 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/equations/linear_advection.py @@ -0,0 +1,38 @@ +# equations/linear_advection.py + +import numpy as np +from .base import Equation + +class LinearAdvectionEquation(Equation): + """ + 线性对流方程: u_t + c * u_x = 0 + 支持标量(num_equations=1) + """ + + def __init__(self, wave_speed: float): + self.c = wave_speed + + @property + def num_equations(self) -> int: + return 1 + + def flux(self, u): + """f(u) = c * u""" + return np.array([self.c * u[0]]) + + def max_wave_speed(self, u): + """最大波速 = |c|""" + return abs(self.c) + + def exact_solution(self, x, t, initial_condition_func): + """ + 解析解: u(x, t) = u0(x - c * t) + 支持周期边界 + """ + if len(x) == 0: + return np.zeros((1, 0)) + + L = x[-1] - x[0] # 假设均匀周期网格 + x_shifted = (x - self.c * t + L) % L + u0 = initial_condition_func(x_shifted) # (1, ncells) + return u0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/flux/__init__.py b/example/1d-linear-convection/weno3/python-v1/01f/flux/__init__.py new file mode 100644 index 00000000..432a36cb --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/flux/__init__.py @@ -0,0 +1,7 @@ +# flux/__init__.py +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 确保子模块被导入以触发注册 +from . import rusanov +from . import engquist_osher \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/flux/base.py b/example/1d-linear-convection/weno3/python-v1/01f/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python-v1/01f/flux/engquist_osher.py new file mode 100644 index 00000000..b65e9d7a --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/flux/engquist_osher.py @@ -0,0 +1,19 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/flux/factory.py b/example/1d-linear-convection/weno3/python-v1/01f/flux/factory.py new file mode 100644 index 00000000..5ae77c3f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/flux/factory.py @@ -0,0 +1,25 @@ +# flux/factory.py +""" +通量计算器专用工厂(封装注册细节,提供清晰接口) +符合你希望“将创建逻辑封装在工厂中”的设计原则 +""" + +from core.registry import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/flux/rusanov.py b/example/1d-linear-convection/weno3/python-v1/01f/flux/rusanov.py new file mode 100644 index 00000000..fdc3c33d --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/flux/rusanov.py @@ -0,0 +1,21 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/initial_condition.py b/example/1d-linear-convection/weno3/python-v1/01f/initial_condition.py new file mode 100644 index 00000000..dda8a3bd --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/initial_condition.py @@ -0,0 +1,90 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from core.registry import ComponentRegistry, register_component + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from core.registry import BaseFactory + return BaseFactory.create_component('initial_condition', config.ic_type, config) + diff --git a/example/1d-linear-convection/weno3/python-v1/01f/mesh.py b/example/1d-linear-convection/weno3/python-v1/01f/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/plotter.py b/example/1d-linear-convection/weno3/python-v1/01f/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/problem.py b/example/1d-linear-convection/weno3/python-v1/01f/problem.py new file mode 100644 index 00000000..53719f26 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/problem.py @@ -0,0 +1,38 @@ +# problem.py +""" +问题定义模块:每个 Problem 子类代表一个完整测试用例 +包含初始条件、解析解(可选)、物理方程等 +""" + +from abc import ABC, abstractmethod +from initial_condition import InitialConditionFactory + + +class Problem(ABC): + """ + 抽象问题基类 + 每个具体问题(如线性对流、Sod 激波管)应继承此类 + """ + def __init__(self, config): + self.config = config + + @abstractmethod + def initial_condition(self): + """ + 返回 InitialCondition 实例 + """ + pass + + def exact_solution(self, cfd): + """ + 可选:返回解析解(数值数组) + 若无解析解,可抛出 NotImplementedError 或返回 None + """ + x = cfd.domain.mesh.xcc + raise NotImplementedError( + f"Problem '{self.__class__.__name__}' does not provide an exact solution." + ) + + @property + def name(self): + return self.__class__.__name__ \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/problems/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/01f/problems/linear_advection.py new file mode 100644 index 00000000..b6d548f5 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/problems/linear_advection.py @@ -0,0 +1,39 @@ +# problems/linear_advection.py + +import numpy as np +from problem import Problem +from equations.linear_advection import LinearAdvectionEquation +from initial_condition import InitialConditionFactory + +class LinearAdvectionProblem(Problem): + """ + 线性对流问题:u_t + c u_x = 0 + 使用周期边界 + 任意初始条件 + """ + + def __init__(self, config): + super().__init__(config) + # 持有物理方程实例 + self.equation = LinearAdvectionEquation(wave_speed=config.wave_speed) + self._ic_cache = None # 缓存 IC 实例 + + def initial_condition(self): + """返回初始条件实例""" + if self._ic_cache is None: + self._ic_cache = InitialConditionFactory.create(self.config) + return self._ic_cache + + def exact_solution(self, cfd): + """ + 委托给 Equation 计算解析解 + """ + ic = self.initial_condition() + # 包装 evaluate_at 以返回 (1, ncells) + def vectorized_ic(x): + u0_scalar = ic.evaluate_at(x) # (ncells,) + return u0_scalar[np.newaxis, :] # (1, ncells) + + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + u_exact = self.equation.exact_solution(x, t, vectorized_ic) + return u_exact[0] # 返回标量数组 (ncells,) 以兼容现有绘图逻辑 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python-v1/01f/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/reconstructor/base.py b/example/1d-linear-convection/weno3/python-v1/01f/reconstructor/base.py new file mode 100644 index 00000000..094b4712 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def compute_face_values(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/reconstructor/eno.py b/example/1d-linear-convection/weno3/python-v1/01f/reconstructor/eno.py new file mode 100644 index 00000000..4c8fc1e4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def compute_face_values(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/reconstructor/factory.py b/example/1d-linear-convection/weno3/python-v1/01f/reconstructor/factory.py new file mode 100644 index 00000000..d0e60a79 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from core.registry import ComponentRegistry, register_component + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from core.registry import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python-v1/01f/reconstructor/weno3.py new file mode 100644 index 00000000..cc7d82df --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/reconstructor/weno3.py @@ -0,0 +1,59 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python-v1/01f/residual.py b/example/1d-linear-convection/weno3/python-v1/01f/residual.py new file mode 100644 index 00000000..e0f96cd3 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux.factory import FluxCalculatorFactory +from reconstructor import ReconstructorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + + self.reconstructor = ReconstructorFactory.create(self.config, self.domain) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._compute_face_values() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _compute_face_values(self): + """私有方法:界面值重建""" + self.reconstructor.compute_face_values(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/result.py b/example/1d-linear-convection/weno3/python-v1/01f/result.py new file mode 100644 index 00000000..5e572216 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/result.py @@ -0,0 +1,25 @@ +# result.py + +class ResultAssembler: + @staticmethod + def assemble(cfd: 'Cfd') -> dict: + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied].copy() + + # ✅ 从 problem 获取解析解(唯一正确路径) + try: + analytical = cfd.problem.exact_solution(cfd) + except NotImplementedError: + analytical = None # 或 np.full_like(u_numerical, np.nan) + + return { + "x": cfd.domain.mesh.xcc, + "numerical": u_numerical, + "analytical": analytical, + "config": { + "scheme": cfd.config.recon_scheme, + "order": cfd.config.spatial_order, + "rk_order": cfd.config.rk_order, + "final_time": cfd.config.final_time, + "problem": cfd.problem.name, + } + } \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/run_eno_weno.py b/example/1d-linear-convection/weno3/python-v1/01f/run_eno_weno.py new file mode 100644 index 00000000..4088266e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/run_eno_weno.py @@ -0,0 +1,51 @@ +# run_eno_weno.py + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +from problems.linear_advection import LinearAdvectionProblem + + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行 ENO3 求解 + print("Running ENO3 solver...") + config_eno3 = CfdConfig() + config_eno3.with_reconstruction("eno", 3) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno3 = LinearAdvectionProblem(config_eno3) + cfd_eno3 = Cfd(problem_eno3, mesh) # ← 注入 problem + cfd_eno3.run() + + # 3. 配置并运行 WENO3 求解 + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno3 = LinearAdvectionProblem(config_weno3) + cfd_weno3 = Cfd(problem_weno3, mesh) # ← 注入 problem + cfd_weno3.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" + ) + + +if __name__ == "__main__": + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/solution.py b/example/1d-linear-convection/weno3/python-v1/01f/solution.py new file mode 100644 index 00000000..90666c34 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/solution.py @@ -0,0 +1,32 @@ +# solution.py +import numpy as np + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/solver.py b/example/1d-linear-convection/weno3/python-v1/01f/solver.py new file mode 100644 index 00000000..ee827a99 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/solver.py @@ -0,0 +1,67 @@ +# solver.py + +from mesh import Mesh +from domain import Domain +from solution import Solution +from config import CfdConfig +from result import ResultAssembler +from problem import Problem +from initial_condition import InitialConditionFactory +from boundary import BoundaryConditionFactory +from residual import ResidualCalculator +from time_integration import TimeIntegrator,TimeIntegratorFactory + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, problem: Problem, mesh): + self.problem = problem + self.config = problem.config + self.domain = Domain(problem.config, mesh) + self.solution = Solution(problem.config, self.domain) + + self.initial_condition = InitialConditionFactory.create(self.config) + self.boundary_condition = BoundaryConditionFactory.create(self) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + + # ==================== 公共接口(供 TimeIntegrator 调用) ==================== + def compute_residual(self): + """计算物理残差(封装重建→通量→散度)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件""" + self.boundary_condition.apply(self.solution.u) + + # ==================== 初始化 ==================== + def initialize(self): + """初始化全场:先 IC,再 BC,最后同步 old field""" + self.initial_condition.apply(self.solution) + # 应用边界条件到初始场 + self.apply_boundary() + # 同步 old field + self.solution.update_old_field() + + + def run(self): + self.initialize() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + self.result = ResultAssembler.assemble(self) + return self.result["numerical"] + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/time_integration/__init__.py b/example/1d-linear-convection/weno3/python-v1/01f/time_integration/__init__.py new file mode 100644 index 00000000..6342de3b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/time_integration/__init__.py @@ -0,0 +1,8 @@ +# time_integration/__init__.py + +# 导出统一接口 +from .factory import TimeIntegratorFactory +from .base import TimeIntegrator + +# 触发子模块注册(关键!) +from . import rk1, rk2, rk3 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/time_integration/base.py b/example/1d-linear-convection/weno3/python-v1/01f/time_integration/base.py new file mode 100644 index 00000000..0cdf29a4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/time_integration/base.py @@ -0,0 +1,27 @@ +# time_integration/base.py + +from abc import ABC, abstractmethod + +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + + @abstractmethod + def step(self, dt): + pass + + def compute_residual(self): + """计算残差(委托给 Cfd)""" + self.cfd.compute_residual() + + def apply_boundary(self): + """应用边界条件(委托给 Cfd)""" + self.cfd.apply_boundary() + + def map_idx(self, i): + """物理网格索引 → 残差数组索引""" + return i - self.domain.ist \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/time_integration/factory.py b/example/1d-linear-convection/weno3/python-v1/01f/time_integration/factory.py new file mode 100644 index 00000000..94662db2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/time_integration/factory.py @@ -0,0 +1,10 @@ +# time_integration/factory.py + +from core.registry import BaseFactory + +class TimeIntegratorFactory: + @staticmethod + def create(cfd) -> 'TimeIntegrator': + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/time_integration/rk1.py b/example/1d-linear-convection/weno3/python-v1/01f/time_integration/rk1.py new file mode 100644 index 00000000..b4c8a021 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/time_integration/rk1.py @@ -0,0 +1,15 @@ +# time_integration/rk1.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/time_integration/rk2.py b/example/1d-linear-convection/weno3/python-v1/01f/time_integration/rk2.py new file mode 100644 index 00000000..6d2be304 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/time_integration/rk2.py @@ -0,0 +1,29 @@ +# time_integration/rk2.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + 0.5 * self.solution.un[i] + + 0.5 * self.solution.u[i] + + 0.5 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01f/time_integration/rk3.py b/example/1d-linear-convection/weno3/python-v1/01f/time_integration/rk3.py new file mode 100644 index 00000000..c70791e1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01f/time_integration/rk3.py @@ -0,0 +1,43 @@ +# time_integration/rk3.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # Stage 1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # Stage 2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = ( + 0.75 * self.solution.un[i] + + 0.25 * self.solution.u[i] + + 0.25 * dt * self.solution.res[j] + ) + self.solution.u[:] = u2 + self.apply_boundary() + + # Stage 3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + c1 * self.solution.un[i] + + c2 * self.solution.u[i] + + c3 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/boundary.py b/example/1d-linear-convection/weno3/python-v1/01g/boundary.py new file mode 100644 index 00000000..2d2cfbaf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/boundary.py @@ -0,0 +1,104 @@ +from abc import ABC, abstractmethod +from core.registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from core.registry import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + diff --git a/example/1d-linear-convection/weno3/python-v1/01g/config.py b/example/1d-linear-convection/weno3/python-v1/01g/config.py new file mode 100644 index 00000000..6e33950f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/config.py @@ -0,0 +1,43 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + #self.ic_type = "sin" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # "rusanov", "engquist-osher" + #self.flux_type = "engquist-osher" # "rusanov", "engquist-osher" + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/core/registry.py b/example/1d-linear-convection/weno3/python-v1/01g/core/registry.py new file mode 100644 index 00000000..a6038a50 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/core/registry.py @@ -0,0 +1,79 @@ +# core/registry.py +""" +统一注册系统 + 通用工厂 +- ComponentRegistry: 组件注册表 +- register_component: 装饰器 +- BaseFactory: 通用工厂类 +""" + +from typing import Dict, Type, Any + + +# ==================== 1. 注册表核心 ==================== +class ComponentRegistry: + """组件注册表""" + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True + + @classmethod + def set_verbose(cls, verbose: bool): + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + if category not in cls._registries: + cls._registries[category] = {} + if name in cls._registries[category]: + if cls._registries[category][name] != component_class and cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + if category not in cls._registries: + raise ValueError(f"❌ 未知类别: {category} (可用: {list(cls._registries.keys())})") + if name not in cls._registries[category]: + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {list(cls._registries[category].keys())})") + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) for cat, comps in cls._registries.items()} + + +# ==================== 2. 装饰器 ==================== +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + +# ==================== 3. 通用工厂 ==================== +class BaseFactory: + """通用工厂基类""" + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + name_lower = name.lower() + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + available = ComponentRegistry.list_all().get(category, []) + if available: + error_msg = f"不支持的 {category} 类型 '{name}'。可用类型:{available}" + else: + error_msg = f"不支持的 {category} 类型 '{name}'(无已注册组件)" + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/domain.py b/example/1d-linear-convection/weno3/python-v1/01g/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/equations/base.py b/example/1d-linear-convection/weno3/python-v1/01g/equations/base.py new file mode 100644 index 00000000..92e54f12 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/equations/base.py @@ -0,0 +1,56 @@ +# equations/base.py + +from abc import ABC, abstractmethod +import numpy as np + +class Equation(ABC): + """ + 控制方程抽象基类 + 所有物理方程(线性对流、Euler、MHD)必须继承此类 + """ + + @property + @abstractmethod + def num_equations(self) -> int: + """返回方程组变量数(标量=1,Euler=3,MHD=8)""" + pass + + @abstractmethod + def flux(self, u): + """ + 计算通量函数 f(u) + :param u: 状态向量 (num_equations,) + :return: 通量向量 (num_equations,) + """ + pass + + @abstractmethod + def max_wave_speed(self, u): + """ + 计算最大波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + @abstractmethod + def wave_speed(self): + """ + 计算波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + def exact_solution(self, x, t, initial_condition_func): + """ + 可选:提供解析解 + 子类可重写;若无解析解,调用方应捕获 NotImplementedError + :param x: 空间坐标数组 (ncells,) + :param t: 时间 + :param initial_condition_func: 初值函数 u0(x) -> (num_equations, ncells) + :return: 解 u(x,t) -> (num_equations, ncells) + """ + raise NotImplementedError( + f"Equation '{self.__class__.__name__}' does not provide an exact solution." + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/equations/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/01g/equations/linear_advection.py new file mode 100644 index 00000000..26aa026b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/equations/linear_advection.py @@ -0,0 +1,42 @@ +# equations/linear_advection.py + +import numpy as np +from .base import Equation + +class LinearAdvectionEquation(Equation): + """ + 线性对流方程: u_t + c * u_x = 0 + 支持标量(num_equations=1) + """ + + def __init__(self, wave_speed: float): + self.c = wave_speed + + @property + def num_equations(self) -> int: + return 1 + + def flux(self, u): + """f(u) = c * u""" + return self.c * u + + def max_wave_speed(self, u): + """最大波速 = |c|""" + return abs(self.c) + + def wave_speed(self): + return self.c + + + def exact_solution(self, x, t, initial_condition_func): + """ + 解析解: u(x, t) = u0(x - c * t) + 支持周期边界 + """ + if len(x) == 0: + return np.zeros((1, 0)) + + L = x[-1] - x[0] # 假设均匀周期网格 + x_shifted = (x - self.c * t + L) % L + u0 = initial_condition_func(x_shifted) # (1, ncells) + return u0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/flux/__init__.py b/example/1d-linear-convection/weno3/python-v1/01g/flux/__init__.py new file mode 100644 index 00000000..432a36cb --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/flux/__init__.py @@ -0,0 +1,7 @@ +# flux/__init__.py +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 确保子模块被导入以触发注册 +from . import rusanov +from . import engquist_osher \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/flux/base.py b/example/1d-linear-convection/weno3/python-v1/01g/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python-v1/01g/flux/engquist_osher.py new file mode 100644 index 00000000..eb7b8cdf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/flux/engquist_osher.py @@ -0,0 +1,20 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + eq = self.cfd.problem.equation + for i in range(self.mesh.nnodes): + c = eq.wave_speed() + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/flux/factory.py b/example/1d-linear-convection/weno3/python-v1/01g/flux/factory.py new file mode 100644 index 00000000..5ae77c3f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/flux/factory.py @@ -0,0 +1,25 @@ +# flux/factory.py +""" +通量计算器专用工厂(封装注册细节,提供清晰接口) +符合你希望“将创建逻辑封装在工厂中”的设计原则 +""" + +from core.registry import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/flux/rusanov.py b/example/1d-linear-convection/weno3/python-v1/01g/flux/rusanov.py new file mode 100644 index 00000000..baa7c62c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/flux/rusanov.py @@ -0,0 +1,25 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量,使用 Equation 解耦物理参数""" + + def compute(self, q_face_left, q_face_right, flux): + # 从 cfd 获取 equation(与 Julia 对齐) + eq = self.cfd.problem.equation + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = eq.wave_speed() + c_R = eq.wave_speed() + # 通过 equation 计算通量和波速 + F_L = eq.flux(u_L) + F_R = eq.flux(u_R) + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/initial_condition.py b/example/1d-linear-convection/weno3/python-v1/01g/initial_condition.py new file mode 100644 index 00000000..dda8a3bd --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/initial_condition.py @@ -0,0 +1,90 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from core.registry import ComponentRegistry, register_component + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from core.registry import BaseFactory + return BaseFactory.create_component('initial_condition', config.ic_type, config) + diff --git a/example/1d-linear-convection/weno3/python-v1/01g/mesh.py b/example/1d-linear-convection/weno3/python-v1/01g/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/plotter.py b/example/1d-linear-convection/weno3/python-v1/01g/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/problem.py b/example/1d-linear-convection/weno3/python-v1/01g/problem.py new file mode 100644 index 00000000..53719f26 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/problem.py @@ -0,0 +1,38 @@ +# problem.py +""" +问题定义模块:每个 Problem 子类代表一个完整测试用例 +包含初始条件、解析解(可选)、物理方程等 +""" + +from abc import ABC, abstractmethod +from initial_condition import InitialConditionFactory + + +class Problem(ABC): + """ + 抽象问题基类 + 每个具体问题(如线性对流、Sod 激波管)应继承此类 + """ + def __init__(self, config): + self.config = config + + @abstractmethod + def initial_condition(self): + """ + 返回 InitialCondition 实例 + """ + pass + + def exact_solution(self, cfd): + """ + 可选:返回解析解(数值数组) + 若无解析解,可抛出 NotImplementedError 或返回 None + """ + x = cfd.domain.mesh.xcc + raise NotImplementedError( + f"Problem '{self.__class__.__name__}' does not provide an exact solution." + ) + + @property + def name(self): + return self.__class__.__name__ \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/problems/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/01g/problems/linear_advection.py new file mode 100644 index 00000000..b6d548f5 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/problems/linear_advection.py @@ -0,0 +1,39 @@ +# problems/linear_advection.py + +import numpy as np +from problem import Problem +from equations.linear_advection import LinearAdvectionEquation +from initial_condition import InitialConditionFactory + +class LinearAdvectionProblem(Problem): + """ + 线性对流问题:u_t + c u_x = 0 + 使用周期边界 + 任意初始条件 + """ + + def __init__(self, config): + super().__init__(config) + # 持有物理方程实例 + self.equation = LinearAdvectionEquation(wave_speed=config.wave_speed) + self._ic_cache = None # 缓存 IC 实例 + + def initial_condition(self): + """返回初始条件实例""" + if self._ic_cache is None: + self._ic_cache = InitialConditionFactory.create(self.config) + return self._ic_cache + + def exact_solution(self, cfd): + """ + 委托给 Equation 计算解析解 + """ + ic = self.initial_condition() + # 包装 evaluate_at 以返回 (1, ncells) + def vectorized_ic(x): + u0_scalar = ic.evaluate_at(x) # (ncells,) + return u0_scalar[np.newaxis, :] # (1, ncells) + + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + u_exact = self.equation.exact_solution(x, t, vectorized_ic) + return u_exact[0] # 返回标量数组 (ncells,) 以兼容现有绘图逻辑 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python-v1/01g/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/reconstructor/base.py b/example/1d-linear-convection/weno3/python-v1/01g/reconstructor/base.py new file mode 100644 index 00000000..094b4712 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def compute_face_values(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/reconstructor/eno.py b/example/1d-linear-convection/weno3/python-v1/01g/reconstructor/eno.py new file mode 100644 index 00000000..4c8fc1e4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def compute_face_values(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/reconstructor/factory.py b/example/1d-linear-convection/weno3/python-v1/01g/reconstructor/factory.py new file mode 100644 index 00000000..d0e60a79 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from core.registry import ComponentRegistry, register_component + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from core.registry import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python-v1/01g/reconstructor/weno3.py new file mode 100644 index 00000000..cc7d82df --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/reconstructor/weno3.py @@ -0,0 +1,59 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python-v1/01g/reconstructor/weno5.py b/example/1d-linear-convection/weno3/python-v1/01g/reconstructor/weno5.py new file mode 100644 index 00000000..415fcfc3 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/reconstructor/weno5.py @@ -0,0 +1,68 @@ +# reconstructor/weno5.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno5') +class Weno5Reconstructor(Reconstructor): + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3, v4, v5) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3, v4, v5) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 1.0/10.0 + d1 = 3.0/5.0 + d2 = 3.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = 1.0/3.0*v1-7.0/6.0*v2+11.0/6.0*v3 # r=2 + q1 = -1.0/6.0*v2+5.0/6.0*v3+1.0/3.0*v4 # r=1 + q2 = 1.0/3.0*v3+5.0/6.0*v4-1.0/6.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 3.0/10.0 + d1 = 3.0/5.0 + d2 = 1.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = -1.0/6.0*v1+5.0/6.0*v2+1.0/3.0*v3 # r=2 + q1 = 1.0/3.0*v2+5.0/6.0*v3-1.0/6.0*v4 # r=1 + q2 = 11.0/6.0*v3-7.0/6.0*v4+1.0/3.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 diff --git a/example/1d-linear-convection/weno3/python-v1/01g/residual.py b/example/1d-linear-convection/weno3/python-v1/01g/residual.py new file mode 100644 index 00000000..e0f96cd3 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux.factory import FluxCalculatorFactory +from reconstructor import ReconstructorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + + self.reconstructor = ReconstructorFactory.create(self.config, self.domain) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._compute_face_values() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _compute_face_values(self): + """私有方法:界面值重建""" + self.reconstructor.compute_face_values(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/result.py b/example/1d-linear-convection/weno3/python-v1/01g/result.py new file mode 100644 index 00000000..5e572216 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/result.py @@ -0,0 +1,25 @@ +# result.py + +class ResultAssembler: + @staticmethod + def assemble(cfd: 'Cfd') -> dict: + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied].copy() + + # ✅ 从 problem 获取解析解(唯一正确路径) + try: + analytical = cfd.problem.exact_solution(cfd) + except NotImplementedError: + analytical = None # 或 np.full_like(u_numerical, np.nan) + + return { + "x": cfd.domain.mesh.xcc, + "numerical": u_numerical, + "analytical": analytical, + "config": { + "scheme": cfd.config.recon_scheme, + "order": cfd.config.spatial_order, + "rk_order": cfd.config.rk_order, + "final_time": cfd.config.final_time, + "problem": cfd.problem.name, + } + } \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/run_eno_weno.py b/example/1d-linear-convection/weno3/python-v1/01g/run_eno_weno.py new file mode 100644 index 00000000..4088266e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/run_eno_weno.py @@ -0,0 +1,51 @@ +# run_eno_weno.py + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +from problems.linear_advection import LinearAdvectionProblem + + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行 ENO3 求解 + print("Running ENO3 solver...") + config_eno3 = CfdConfig() + config_eno3.with_reconstruction("eno", 3) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno3 = LinearAdvectionProblem(config_eno3) + cfd_eno3 = Cfd(problem_eno3, mesh) # ← 注入 problem + cfd_eno3.run() + + # 3. 配置并运行 WENO3 求解 + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno3 = LinearAdvectionProblem(config_weno3) + cfd_weno3 = Cfd(problem_weno3, mesh) # ← 注入 problem + cfd_weno3.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" + ) + + +if __name__ == "__main__": + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/solution.py b/example/1d-linear-convection/weno3/python-v1/01g/solution.py new file mode 100644 index 00000000..90666c34 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/solution.py @@ -0,0 +1,32 @@ +# solution.py +import numpy as np + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/solver.py b/example/1d-linear-convection/weno3/python-v1/01g/solver.py new file mode 100644 index 00000000..316be231 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/solver.py @@ -0,0 +1,67 @@ +# solver.py + +from mesh import Mesh +from domain import Domain +from solution import Solution +from config import CfdConfig +from result import ResultAssembler +from problem import Problem +from initial_condition import InitialConditionFactory +from boundary import BoundaryConditionFactory +from residual import ResidualCalculator +from time_integration import TimeIntegrator,TimeIntegratorFactory + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, problem: Problem, mesh): + self.problem = problem + self.config = problem.config + self.domain = Domain(problem.config, mesh) + self.solution = Solution(problem.config, self.domain) + + self.initial_condition = InitialConditionFactory.create(self.config) + self.boundary_condition = BoundaryConditionFactory.create(self) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + + # ==================== 公共接口(供 TimeIntegrator 调用) ==================== + def compute_residual(self): + """计算物理残差(封装重建→通量→散度)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件""" + self.boundary_condition.apply(self.solution.u) + + # ==================== 初始化 ==================== + def initialize(self): + """初始化全场:先 IC,再 BC,最后同步 old field""" + self.initial_condition.apply(self.solution) + # 应用边界条件到初始场 + self.apply_boundary() + # 同步 old field + self.solution.update_old_field() + + + def run(self): + self.initialize() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + self.result = ResultAssembler.assemble(self) + return self.result["numerical"] + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/time_integration/__init__.py b/example/1d-linear-convection/weno3/python-v1/01g/time_integration/__init__.py new file mode 100644 index 00000000..6342de3b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/time_integration/__init__.py @@ -0,0 +1,8 @@ +# time_integration/__init__.py + +# 导出统一接口 +from .factory import TimeIntegratorFactory +from .base import TimeIntegrator + +# 触发子模块注册(关键!) +from . import rk1, rk2, rk3 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/time_integration/base.py b/example/1d-linear-convection/weno3/python-v1/01g/time_integration/base.py new file mode 100644 index 00000000..0cdf29a4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/time_integration/base.py @@ -0,0 +1,27 @@ +# time_integration/base.py + +from abc import ABC, abstractmethod + +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + + @abstractmethod + def step(self, dt): + pass + + def compute_residual(self): + """计算残差(委托给 Cfd)""" + self.cfd.compute_residual() + + def apply_boundary(self): + """应用边界条件(委托给 Cfd)""" + self.cfd.apply_boundary() + + def map_idx(self, i): + """物理网格索引 → 残差数组索引""" + return i - self.domain.ist \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/time_integration/factory.py b/example/1d-linear-convection/weno3/python-v1/01g/time_integration/factory.py new file mode 100644 index 00000000..94662db2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/time_integration/factory.py @@ -0,0 +1,10 @@ +# time_integration/factory.py + +from core.registry import BaseFactory + +class TimeIntegratorFactory: + @staticmethod + def create(cfd) -> 'TimeIntegrator': + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/time_integration/rk1.py b/example/1d-linear-convection/weno3/python-v1/01g/time_integration/rk1.py new file mode 100644 index 00000000..b4c8a021 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/time_integration/rk1.py @@ -0,0 +1,15 @@ +# time_integration/rk1.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/time_integration/rk2.py b/example/1d-linear-convection/weno3/python-v1/01g/time_integration/rk2.py new file mode 100644 index 00000000..6d2be304 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/time_integration/rk2.py @@ -0,0 +1,29 @@ +# time_integration/rk2.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + 0.5 * self.solution.un[i] + + 0.5 * self.solution.u[i] + + 0.5 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01g/time_integration/rk3.py b/example/1d-linear-convection/weno3/python-v1/01g/time_integration/rk3.py new file mode 100644 index 00000000..c70791e1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01g/time_integration/rk3.py @@ -0,0 +1,43 @@ +# time_integration/rk3.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # Stage 1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # Stage 2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = ( + 0.75 * self.solution.un[i] + + 0.25 * self.solution.u[i] + + 0.25 * dt * self.solution.res[j] + ) + self.solution.u[:] = u2 + self.apply_boundary() + + # Stage 3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + c1 * self.solution.un[i] + + c2 * self.solution.u[i] + + c3 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/boundary.py b/example/1d-linear-convection/weno3/python-v1/01h/boundary.py new file mode 100644 index 00000000..2d2cfbaf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/boundary.py @@ -0,0 +1,104 @@ +from abc import ABC, abstractmethod +from core.registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from core.registry import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + diff --git a/example/1d-linear-convection/weno3/python-v1/01h/config.py b/example/1d-linear-convection/weno3/python-v1/01h/config.py new file mode 100644 index 00000000..6432c80e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/config.py @@ -0,0 +1,45 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + #self.ic_type = "sin" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # "rusanov", "engquist-osher" + #self.flux_type = "engquist-osher" # "rusanov", "engquist-osher" + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + print(f"scheme={scheme}") + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/core/registry.py b/example/1d-linear-convection/weno3/python-v1/01h/core/registry.py new file mode 100644 index 00000000..4a52afc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/core/registry.py @@ -0,0 +1,83 @@ +# core/registry.py +""" +统一注册系统 + 通用工厂 +- ComponentRegistry: 组件注册表 +- register_component: 装饰器 +- BaseFactory: 通用工厂类 +""" + +from typing import Dict, Type, Any + + +# ==================== 1. 注册表核心 ==================== +class ComponentRegistry: + """组件注册表""" + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True + + @classmethod + def set_verbose(cls, verbose: bool): + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + #print(f"ComponentRegistry register cls={cls}") + #print(f"ComponentRegistry register name={name}") + #print(f"ComponentRegistry register component_class={component_class}") + if category not in cls._registries: + cls._registries[category] = {} + if name in cls._registries[category]: + if cls._registries[category][name] != component_class and cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + if category not in cls._registries: + raise ValueError(f"❌ 未知类别: {category} (可用: {list(cls._registries.keys())})") + if name not in cls._registries[category]: + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {list(cls._registries[category].keys())})") + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) for cat, comps in cls._registries.items()} + + +# ==================== 2. 装饰器 ==================== +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + #print(f"register_component decorator name={name}") + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + +# ==================== 3. 通用工厂 ==================== +class BaseFactory: + """通用工厂基类""" + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + name_lower = name.lower() + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + available = ComponentRegistry.list_all().get(category, []) + if available: + error_msg = f"不支持的 {category} 类型 '{name}'。可用类型:{available}" + else: + error_msg = f"不支持的 {category} 类型 '{name}'(无已注册组件)" + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/domain.py b/example/1d-linear-convection/weno3/python-v1/01h/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/equations/base.py b/example/1d-linear-convection/weno3/python-v1/01h/equations/base.py new file mode 100644 index 00000000..92e54f12 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/equations/base.py @@ -0,0 +1,56 @@ +# equations/base.py + +from abc import ABC, abstractmethod +import numpy as np + +class Equation(ABC): + """ + 控制方程抽象基类 + 所有物理方程(线性对流、Euler、MHD)必须继承此类 + """ + + @property + @abstractmethod + def num_equations(self) -> int: + """返回方程组变量数(标量=1,Euler=3,MHD=8)""" + pass + + @abstractmethod + def flux(self, u): + """ + 计算通量函数 f(u) + :param u: 状态向量 (num_equations,) + :return: 通量向量 (num_equations,) + """ + pass + + @abstractmethod + def max_wave_speed(self, u): + """ + 计算最大波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + @abstractmethod + def wave_speed(self): + """ + 计算波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + def exact_solution(self, x, t, initial_condition_func): + """ + 可选:提供解析解 + 子类可重写;若无解析解,调用方应捕获 NotImplementedError + :param x: 空间坐标数组 (ncells,) + :param t: 时间 + :param initial_condition_func: 初值函数 u0(x) -> (num_equations, ncells) + :return: 解 u(x,t) -> (num_equations, ncells) + """ + raise NotImplementedError( + f"Equation '{self.__class__.__name__}' does not provide an exact solution." + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/equations/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/01h/equations/linear_advection.py new file mode 100644 index 00000000..26aa026b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/equations/linear_advection.py @@ -0,0 +1,42 @@ +# equations/linear_advection.py + +import numpy as np +from .base import Equation + +class LinearAdvectionEquation(Equation): + """ + 线性对流方程: u_t + c * u_x = 0 + 支持标量(num_equations=1) + """ + + def __init__(self, wave_speed: float): + self.c = wave_speed + + @property + def num_equations(self) -> int: + return 1 + + def flux(self, u): + """f(u) = c * u""" + return self.c * u + + def max_wave_speed(self, u): + """最大波速 = |c|""" + return abs(self.c) + + def wave_speed(self): + return self.c + + + def exact_solution(self, x, t, initial_condition_func): + """ + 解析解: u(x, t) = u0(x - c * t) + 支持周期边界 + """ + if len(x) == 0: + return np.zeros((1, 0)) + + L = x[-1] - x[0] # 假设均匀周期网格 + x_shifted = (x - self.c * t + L) % L + u0 = initial_condition_func(x_shifted) # (1, ncells) + return u0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/flux/__init__.py b/example/1d-linear-convection/weno3/python-v1/01h/flux/__init__.py new file mode 100644 index 00000000..432a36cb --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/flux/__init__.py @@ -0,0 +1,7 @@ +# flux/__init__.py +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 确保子模块被导入以触发注册 +from . import rusanov +from . import engquist_osher \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/flux/base.py b/example/1d-linear-convection/weno3/python-v1/01h/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python-v1/01h/flux/engquist_osher.py new file mode 100644 index 00000000..eb7b8cdf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/flux/engquist_osher.py @@ -0,0 +1,20 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + eq = self.cfd.problem.equation + for i in range(self.mesh.nnodes): + c = eq.wave_speed() + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/flux/factory.py b/example/1d-linear-convection/weno3/python-v1/01h/flux/factory.py new file mode 100644 index 00000000..5ae77c3f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/flux/factory.py @@ -0,0 +1,25 @@ +# flux/factory.py +""" +通量计算器专用工厂(封装注册细节,提供清晰接口) +符合你希望“将创建逻辑封装在工厂中”的设计原则 +""" + +from core.registry import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/flux/rusanov.py b/example/1d-linear-convection/weno3/python-v1/01h/flux/rusanov.py new file mode 100644 index 00000000..baa7c62c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/flux/rusanov.py @@ -0,0 +1,25 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量,使用 Equation 解耦物理参数""" + + def compute(self, q_face_left, q_face_right, flux): + # 从 cfd 获取 equation(与 Julia 对齐) + eq = self.cfd.problem.equation + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = eq.wave_speed() + c_R = eq.wave_speed() + # 通过 equation 计算通量和波速 + F_L = eq.flux(u_L) + F_R = eq.flux(u_R) + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/initial_condition.py b/example/1d-linear-convection/weno3/python-v1/01h/initial_condition.py new file mode 100644 index 00000000..dda8a3bd --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/initial_condition.py @@ -0,0 +1,90 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from core.registry import ComponentRegistry, register_component + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from core.registry import BaseFactory + return BaseFactory.create_component('initial_condition', config.ic_type, config) + diff --git a/example/1d-linear-convection/weno3/python-v1/01h/mesh.py b/example/1d-linear-convection/weno3/python-v1/01h/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/plotter.py b/example/1d-linear-convection/weno3/python-v1/01h/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/problem.py b/example/1d-linear-convection/weno3/python-v1/01h/problem.py new file mode 100644 index 00000000..53719f26 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/problem.py @@ -0,0 +1,38 @@ +# problem.py +""" +问题定义模块:每个 Problem 子类代表一个完整测试用例 +包含初始条件、解析解(可选)、物理方程等 +""" + +from abc import ABC, abstractmethod +from initial_condition import InitialConditionFactory + + +class Problem(ABC): + """ + 抽象问题基类 + 每个具体问题(如线性对流、Sod 激波管)应继承此类 + """ + def __init__(self, config): + self.config = config + + @abstractmethod + def initial_condition(self): + """ + 返回 InitialCondition 实例 + """ + pass + + def exact_solution(self, cfd): + """ + 可选:返回解析解(数值数组) + 若无解析解,可抛出 NotImplementedError 或返回 None + """ + x = cfd.domain.mesh.xcc + raise NotImplementedError( + f"Problem '{self.__class__.__name__}' does not provide an exact solution." + ) + + @property + def name(self): + return self.__class__.__name__ \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/problems/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/01h/problems/linear_advection.py new file mode 100644 index 00000000..b6d548f5 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/problems/linear_advection.py @@ -0,0 +1,39 @@ +# problems/linear_advection.py + +import numpy as np +from problem import Problem +from equations.linear_advection import LinearAdvectionEquation +from initial_condition import InitialConditionFactory + +class LinearAdvectionProblem(Problem): + """ + 线性对流问题:u_t + c u_x = 0 + 使用周期边界 + 任意初始条件 + """ + + def __init__(self, config): + super().__init__(config) + # 持有物理方程实例 + self.equation = LinearAdvectionEquation(wave_speed=config.wave_speed) + self._ic_cache = None # 缓存 IC 实例 + + def initial_condition(self): + """返回初始条件实例""" + if self._ic_cache is None: + self._ic_cache = InitialConditionFactory.create(self.config) + return self._ic_cache + + def exact_solution(self, cfd): + """ + 委托给 Equation 计算解析解 + """ + ic = self.initial_condition() + # 包装 evaluate_at 以返回 (1, ncells) + def vectorized_ic(x): + u0_scalar = ic.evaluate_at(x) # (ncells,) + return u0_scalar[np.newaxis, :] # (1, ncells) + + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + u_exact = self.equation.exact_solution(x, t, vectorized_ic) + return u_exact[0] # 返回标量数组 (ncells,) 以兼容现有绘图逻辑 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python-v1/01h/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/reconstructor/base.py b/example/1d-linear-convection/weno3/python-v1/01h/reconstructor/base.py new file mode 100644 index 00000000..094b4712 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def compute_face_values(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/reconstructor/eno.py b/example/1d-linear-convection/weno3/python-v1/01h/reconstructor/eno.py new file mode 100644 index 00000000..4c8fc1e4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def compute_face_values(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/reconstructor/factory.py b/example/1d-linear-convection/weno3/python-v1/01h/reconstructor/factory.py new file mode 100644 index 00000000..9e7f4680 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/reconstructor/factory.py @@ -0,0 +1,36 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +#from .weno3 import Weno3Reconstructor +from .weno5 import Weno5Reconstructor +from core.registry import ComponentRegistry, register_component + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + print(f"ReconstructorFactory scheme={scheme}") + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + print(f"ReconstructorFactory scheme={scheme}") + # 使用BaseFactory,但处理特殊参数 + from core.registry import BaseFactory + + print(f"scheme={scheme}") + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3" or scheme == "weno5": + # WENO3,WENO5无参数 + print(f"scheme == 'eno3' or scheme == 'weno5' scheme={scheme}") + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python-v1/01h/reconstructor/weno3.py new file mode 100644 index 00000000..cc7d82df --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/reconstructor/weno3.py @@ -0,0 +1,59 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python-v1/01h/reconstructor/weno5.py b/example/1d-linear-convection/weno3/python-v1/01h/reconstructor/weno5.py new file mode 100644 index 00000000..af5ca809 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/reconstructor/weno5.py @@ -0,0 +1,68 @@ +# reconstructor/weno5.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno5') +class Weno5Reconstructor(Reconstructor): + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3, v4, v5) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3, v4, v5) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 1.0/10.0 + d1 = 3.0/5.0 + d2 = 3.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = 1.0/3.0*v1-7.0/6.0*v2+11.0/6.0*v3 # r=2 + q1 = -1.0/6.0*v2+5.0/6.0*v3+1.0/3.0*v4 # r=1 + q2 = 1.0/3.0*v3+5.0/6.0*v4-1.0/6.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 3.0/10.0 + d1 = 3.0/5.0 + d2 = 1.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = -1.0/6.0*v1+5.0/6.0*v2+1.0/3.0*v3 # r=2 + q1 = 1.0/3.0*v2+5.0/6.0*v3-1.0/6.0*v4 # r=1 + q2 = 11.0/6.0*v3-7.0/6.0*v4+1.0/3.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 diff --git a/example/1d-linear-convection/weno3/python-v1/01h/residual.py b/example/1d-linear-convection/weno3/python-v1/01h/residual.py new file mode 100644 index 00000000..e0f96cd3 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux.factory import FluxCalculatorFactory +from reconstructor import ReconstructorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + + self.reconstructor = ReconstructorFactory.create(self.config, self.domain) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._compute_face_values() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _compute_face_values(self): + """私有方法:界面值重建""" + self.reconstructor.compute_face_values(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/result.py b/example/1d-linear-convection/weno3/python-v1/01h/result.py new file mode 100644 index 00000000..5e572216 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/result.py @@ -0,0 +1,25 @@ +# result.py + +class ResultAssembler: + @staticmethod + def assemble(cfd: 'Cfd') -> dict: + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied].copy() + + # ✅ 从 problem 获取解析解(唯一正确路径) + try: + analytical = cfd.problem.exact_solution(cfd) + except NotImplementedError: + analytical = None # 或 np.full_like(u_numerical, np.nan) + + return { + "x": cfd.domain.mesh.xcc, + "numerical": u_numerical, + "analytical": analytical, + "config": { + "scheme": cfd.config.recon_scheme, + "order": cfd.config.spatial_order, + "rk_order": cfd.config.rk_order, + "final_time": cfd.config.final_time, + "problem": cfd.problem.name, + } + } \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/run_eno_weno.py b/example/1d-linear-convection/weno3/python-v1/01h/run_eno_weno.py new file mode 100644 index 00000000..ea83cbe8 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/run_eno_weno.py @@ -0,0 +1,88 @@ +# run_eno_weno.py + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +from problems.linear_advection import LinearAdvectionProblem + + +def performEnoWenoAnalysisBAK(): + # 1. 初始化网格 + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行 ENO3 求解 + print("Running ENO3 solver...") + config_eno3 = CfdConfig() + config_eno3.with_reconstruction("eno", 3) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno3 = LinearAdvectionProblem(config_eno3) + cfd_eno3 = Cfd(problem_eno3, mesh) # ← 注入 problem + cfd_eno3.run() + + # 3. 配置并运行 WENO3 求解 + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno3 = LinearAdvectionProblem(config_weno3) + cfd_weno3 = Cfd(problem_weno3, mesh) # ← 注入 problem + cfd_weno3.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" + ) + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行 ENO5 求解 + print("Running ENO5 solver...") + config_eno5 = CfdConfig() + config_eno5.with_reconstruction("eno", 5) + config_eno5.dt = 0.0025 + config_eno5.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno5 = LinearAdvectionProblem(config_eno5) + cfd_eno5 = Cfd(problem_eno5, mesh) # ← 注入 problem + cfd_eno5.run() + + # 3. 配置并运行 WENO5求解 + print("Running WENO5 solver...") + config_weno5 = CfdConfig() + config_weno5.with_reconstruction("weno", 5) + config_weno5.dt = 0.0025 + config_weno5.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno5 = LinearAdvectionProblem(config_weno5) + cfd_weno5 = Cfd(problem_weno5, mesh) # ← 注入 problem + cfd_weno5.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno5.result, + weno_result=cfd_weno5.result, + save_path="eno_weno_comparison.png" + ) + + +if __name__ == "__main__": + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/solution.py b/example/1d-linear-convection/weno3/python-v1/01h/solution.py new file mode 100644 index 00000000..90666c34 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/solution.py @@ -0,0 +1,32 @@ +# solution.py +import numpy as np + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/solver.py b/example/1d-linear-convection/weno3/python-v1/01h/solver.py new file mode 100644 index 00000000..316be231 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/solver.py @@ -0,0 +1,67 @@ +# solver.py + +from mesh import Mesh +from domain import Domain +from solution import Solution +from config import CfdConfig +from result import ResultAssembler +from problem import Problem +from initial_condition import InitialConditionFactory +from boundary import BoundaryConditionFactory +from residual import ResidualCalculator +from time_integration import TimeIntegrator,TimeIntegratorFactory + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, problem: Problem, mesh): + self.problem = problem + self.config = problem.config + self.domain = Domain(problem.config, mesh) + self.solution = Solution(problem.config, self.domain) + + self.initial_condition = InitialConditionFactory.create(self.config) + self.boundary_condition = BoundaryConditionFactory.create(self) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + + # ==================== 公共接口(供 TimeIntegrator 调用) ==================== + def compute_residual(self): + """计算物理残差(封装重建→通量→散度)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件""" + self.boundary_condition.apply(self.solution.u) + + # ==================== 初始化 ==================== + def initialize(self): + """初始化全场:先 IC,再 BC,最后同步 old field""" + self.initial_condition.apply(self.solution) + # 应用边界条件到初始场 + self.apply_boundary() + # 同步 old field + self.solution.update_old_field() + + + def run(self): + self.initialize() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + self.result = ResultAssembler.assemble(self) + return self.result["numerical"] + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/time_integration/__init__.py b/example/1d-linear-convection/weno3/python-v1/01h/time_integration/__init__.py new file mode 100644 index 00000000..6342de3b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/time_integration/__init__.py @@ -0,0 +1,8 @@ +# time_integration/__init__.py + +# 导出统一接口 +from .factory import TimeIntegratorFactory +from .base import TimeIntegrator + +# 触发子模块注册(关键!) +from . import rk1, rk2, rk3 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/time_integration/base.py b/example/1d-linear-convection/weno3/python-v1/01h/time_integration/base.py new file mode 100644 index 00000000..0cdf29a4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/time_integration/base.py @@ -0,0 +1,27 @@ +# time_integration/base.py + +from abc import ABC, abstractmethod + +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + + @abstractmethod + def step(self, dt): + pass + + def compute_residual(self): + """计算残差(委托给 Cfd)""" + self.cfd.compute_residual() + + def apply_boundary(self): + """应用边界条件(委托给 Cfd)""" + self.cfd.apply_boundary() + + def map_idx(self, i): + """物理网格索引 → 残差数组索引""" + return i - self.domain.ist \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/time_integration/factory.py b/example/1d-linear-convection/weno3/python-v1/01h/time_integration/factory.py new file mode 100644 index 00000000..94662db2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/time_integration/factory.py @@ -0,0 +1,10 @@ +# time_integration/factory.py + +from core.registry import BaseFactory + +class TimeIntegratorFactory: + @staticmethod + def create(cfd) -> 'TimeIntegrator': + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/time_integration/rk1.py b/example/1d-linear-convection/weno3/python-v1/01h/time_integration/rk1.py new file mode 100644 index 00000000..b4c8a021 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/time_integration/rk1.py @@ -0,0 +1,15 @@ +# time_integration/rk1.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/time_integration/rk2.py b/example/1d-linear-convection/weno3/python-v1/01h/time_integration/rk2.py new file mode 100644 index 00000000..6d2be304 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/time_integration/rk2.py @@ -0,0 +1,29 @@ +# time_integration/rk2.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + 0.5 * self.solution.un[i] + + 0.5 * self.solution.u[i] + + 0.5 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01h/time_integration/rk3.py b/example/1d-linear-convection/weno3/python-v1/01h/time_integration/rk3.py new file mode 100644 index 00000000..c70791e1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01h/time_integration/rk3.py @@ -0,0 +1,43 @@ +# time_integration/rk3.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # Stage 1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # Stage 2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = ( + 0.75 * self.solution.un[i] + + 0.25 * self.solution.u[i] + + 0.25 * dt * self.solution.res[j] + ) + self.solution.u[:] = u2 + self.apply_boundary() + + # Stage 3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + c1 * self.solution.un[i] + + c2 * self.solution.u[i] + + c3 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/boundary.py b/example/1d-linear-convection/weno3/python-v1/01i/boundary.py new file mode 100644 index 00000000..2d2cfbaf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/boundary.py @@ -0,0 +1,104 @@ +from abc import ABC, abstractmethod +from core.registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from core.registry import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + diff --git a/example/1d-linear-convection/weno3/python-v1/01i/config.py b/example/1d-linear-convection/weno3/python-v1/01i/config.py new file mode 100644 index 00000000..6432c80e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/config.py @@ -0,0 +1,45 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + #self.ic_type = "sin" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # "rusanov", "engquist-osher" + #self.flux_type = "engquist-osher" # "rusanov", "engquist-osher" + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + print(f"scheme={scheme}") + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/core/registry.py b/example/1d-linear-convection/weno3/python-v1/01i/core/registry.py new file mode 100644 index 00000000..4a52afc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/core/registry.py @@ -0,0 +1,83 @@ +# core/registry.py +""" +统一注册系统 + 通用工厂 +- ComponentRegistry: 组件注册表 +- register_component: 装饰器 +- BaseFactory: 通用工厂类 +""" + +from typing import Dict, Type, Any + + +# ==================== 1. 注册表核心 ==================== +class ComponentRegistry: + """组件注册表""" + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True + + @classmethod + def set_verbose(cls, verbose: bool): + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + #print(f"ComponentRegistry register cls={cls}") + #print(f"ComponentRegistry register name={name}") + #print(f"ComponentRegistry register component_class={component_class}") + if category not in cls._registries: + cls._registries[category] = {} + if name in cls._registries[category]: + if cls._registries[category][name] != component_class and cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + if category not in cls._registries: + raise ValueError(f"❌ 未知类别: {category} (可用: {list(cls._registries.keys())})") + if name not in cls._registries[category]: + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {list(cls._registries[category].keys())})") + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) for cat, comps in cls._registries.items()} + + +# ==================== 2. 装饰器 ==================== +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + #print(f"register_component decorator name={name}") + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + +# ==================== 3. 通用工厂 ==================== +class BaseFactory: + """通用工厂基类""" + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + name_lower = name.lower() + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + available = ComponentRegistry.list_all().get(category, []) + if available: + error_msg = f"不支持的 {category} 类型 '{name}'。可用类型:{available}" + else: + error_msg = f"不支持的 {category} 类型 '{name}'(无已注册组件)" + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/domain.py b/example/1d-linear-convection/weno3/python-v1/01i/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/equations/base.py b/example/1d-linear-convection/weno3/python-v1/01i/equations/base.py new file mode 100644 index 00000000..92e54f12 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/equations/base.py @@ -0,0 +1,56 @@ +# equations/base.py + +from abc import ABC, abstractmethod +import numpy as np + +class Equation(ABC): + """ + 控制方程抽象基类 + 所有物理方程(线性对流、Euler、MHD)必须继承此类 + """ + + @property + @abstractmethod + def num_equations(self) -> int: + """返回方程组变量数(标量=1,Euler=3,MHD=8)""" + pass + + @abstractmethod + def flux(self, u): + """ + 计算通量函数 f(u) + :param u: 状态向量 (num_equations,) + :return: 通量向量 (num_equations,) + """ + pass + + @abstractmethod + def max_wave_speed(self, u): + """ + 计算最大波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + @abstractmethod + def wave_speed(self): + """ + 计算波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + def exact_solution(self, x, t, initial_condition_func): + """ + 可选:提供解析解 + 子类可重写;若无解析解,调用方应捕获 NotImplementedError + :param x: 空间坐标数组 (ncells,) + :param t: 时间 + :param initial_condition_func: 初值函数 u0(x) -> (num_equations, ncells) + :return: 解 u(x,t) -> (num_equations, ncells) + """ + raise NotImplementedError( + f"Equation '{self.__class__.__name__}' does not provide an exact solution." + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/equations/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/01i/equations/linear_advection.py new file mode 100644 index 00000000..26aa026b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/equations/linear_advection.py @@ -0,0 +1,42 @@ +# equations/linear_advection.py + +import numpy as np +from .base import Equation + +class LinearAdvectionEquation(Equation): + """ + 线性对流方程: u_t + c * u_x = 0 + 支持标量(num_equations=1) + """ + + def __init__(self, wave_speed: float): + self.c = wave_speed + + @property + def num_equations(self) -> int: + return 1 + + def flux(self, u): + """f(u) = c * u""" + return self.c * u + + def max_wave_speed(self, u): + """最大波速 = |c|""" + return abs(self.c) + + def wave_speed(self): + return self.c + + + def exact_solution(self, x, t, initial_condition_func): + """ + 解析解: u(x, t) = u0(x - c * t) + 支持周期边界 + """ + if len(x) == 0: + return np.zeros((1, 0)) + + L = x[-1] - x[0] # 假设均匀周期网格 + x_shifted = (x - self.c * t + L) % L + u0 = initial_condition_func(x_shifted) # (1, ncells) + return u0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/flux/__init__.py b/example/1d-linear-convection/weno3/python-v1/01i/flux/__init__.py new file mode 100644 index 00000000..432a36cb --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/flux/__init__.py @@ -0,0 +1,7 @@ +# flux/__init__.py +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 确保子模块被导入以触发注册 +from . import rusanov +from . import engquist_osher \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/flux/base.py b/example/1d-linear-convection/weno3/python-v1/01i/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python-v1/01i/flux/engquist_osher.py new file mode 100644 index 00000000..eb7b8cdf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/flux/engquist_osher.py @@ -0,0 +1,20 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + eq = self.cfd.problem.equation + for i in range(self.mesh.nnodes): + c = eq.wave_speed() + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/flux/factory.py b/example/1d-linear-convection/weno3/python-v1/01i/flux/factory.py new file mode 100644 index 00000000..5ae77c3f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/flux/factory.py @@ -0,0 +1,25 @@ +# flux/factory.py +""" +通量计算器专用工厂(封装注册细节,提供清晰接口) +符合你希望“将创建逻辑封装在工厂中”的设计原则 +""" + +from core.registry import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/flux/rusanov.py b/example/1d-linear-convection/weno3/python-v1/01i/flux/rusanov.py new file mode 100644 index 00000000..baa7c62c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/flux/rusanov.py @@ -0,0 +1,25 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量,使用 Equation 解耦物理参数""" + + def compute(self, q_face_left, q_face_right, flux): + # 从 cfd 获取 equation(与 Julia 对齐) + eq = self.cfd.problem.equation + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = eq.wave_speed() + c_R = eq.wave_speed() + # 通过 equation 计算通量和波速 + F_L = eq.flux(u_L) + F_R = eq.flux(u_R) + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/initial_condition.py b/example/1d-linear-convection/weno3/python-v1/01i/initial_condition.py new file mode 100644 index 00000000..dda8a3bd --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/initial_condition.py @@ -0,0 +1,90 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from core.registry import ComponentRegistry, register_component + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from core.registry import BaseFactory + return BaseFactory.create_component('initial_condition', config.ic_type, config) + diff --git a/example/1d-linear-convection/weno3/python-v1/01i/mesh.py b/example/1d-linear-convection/weno3/python-v1/01i/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/plotter.py b/example/1d-linear-convection/weno3/python-v1/01i/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/problem.py b/example/1d-linear-convection/weno3/python-v1/01i/problem.py new file mode 100644 index 00000000..53719f26 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/problem.py @@ -0,0 +1,38 @@ +# problem.py +""" +问题定义模块:每个 Problem 子类代表一个完整测试用例 +包含初始条件、解析解(可选)、物理方程等 +""" + +from abc import ABC, abstractmethod +from initial_condition import InitialConditionFactory + + +class Problem(ABC): + """ + 抽象问题基类 + 每个具体问题(如线性对流、Sod 激波管)应继承此类 + """ + def __init__(self, config): + self.config = config + + @abstractmethod + def initial_condition(self): + """ + 返回 InitialCondition 实例 + """ + pass + + def exact_solution(self, cfd): + """ + 可选:返回解析解(数值数组) + 若无解析解,可抛出 NotImplementedError 或返回 None + """ + x = cfd.domain.mesh.xcc + raise NotImplementedError( + f"Problem '{self.__class__.__name__}' does not provide an exact solution." + ) + + @property + def name(self): + return self.__class__.__name__ \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/problems/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/01i/problems/linear_advection.py new file mode 100644 index 00000000..b6d548f5 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/problems/linear_advection.py @@ -0,0 +1,39 @@ +# problems/linear_advection.py + +import numpy as np +from problem import Problem +from equations.linear_advection import LinearAdvectionEquation +from initial_condition import InitialConditionFactory + +class LinearAdvectionProblem(Problem): + """ + 线性对流问题:u_t + c u_x = 0 + 使用周期边界 + 任意初始条件 + """ + + def __init__(self, config): + super().__init__(config) + # 持有物理方程实例 + self.equation = LinearAdvectionEquation(wave_speed=config.wave_speed) + self._ic_cache = None # 缓存 IC 实例 + + def initial_condition(self): + """返回初始条件实例""" + if self._ic_cache is None: + self._ic_cache = InitialConditionFactory.create(self.config) + return self._ic_cache + + def exact_solution(self, cfd): + """ + 委托给 Equation 计算解析解 + """ + ic = self.initial_condition() + # 包装 evaluate_at 以返回 (1, ncells) + def vectorized_ic(x): + u0_scalar = ic.evaluate_at(x) # (ncells,) + return u0_scalar[np.newaxis, :] # (1, ncells) + + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + u_exact = self.equation.exact_solution(x, t, vectorized_ic) + return u_exact[0] # 返回标量数组 (ncells,) 以兼容现有绘图逻辑 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python-v1/01i/reconstructor/__init__.py new file mode 100644 index 00000000..d705bb98 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/reconstructor/__init__.py @@ -0,0 +1,6 @@ +# reconstructor/__init__.py +from .base import Reconstructor +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from .weno5 import Weno5Reconstructor +from .factory import ReconstructorFactory diff --git a/example/1d-linear-convection/weno3/python-v1/01i/reconstructor/base.py b/example/1d-linear-convection/weno3/python-v1/01i/reconstructor/base.py new file mode 100644 index 00000000..094b4712 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def compute_face_values(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/reconstructor/eno.py b/example/1d-linear-convection/weno3/python-v1/01i/reconstructor/eno.py new file mode 100644 index 00000000..4c8fc1e4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def compute_face_values(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/reconstructor/factory.py b/example/1d-linear-convection/weno3/python-v1/01i/reconstructor/factory.py new file mode 100644 index 00000000..25e755e0 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/reconstructor/factory.py @@ -0,0 +1,28 @@ +# reconstructor/factory.py +from core.registry import BaseFactory + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + print(f"ReconstructorFactory scheme={scheme}") + + print(f"scheme={scheme}") + if scheme == "eno": + if order is None: + order = 3 + return BaseFactory.create_component('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3" or scheme == "weno5": + # WENO3,WENO5无参数 + print(f"scheme == 'eno3' or scheme == 'weno5' scheme={scheme}") + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python-v1/01i/reconstructor/weno3.py new file mode 100644 index 00000000..cc7d82df --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/reconstructor/weno3.py @@ -0,0 +1,59 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python-v1/01i/reconstructor/weno5.py b/example/1d-linear-convection/weno3/python-v1/01i/reconstructor/weno5.py new file mode 100644 index 00000000..af5ca809 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/reconstructor/weno5.py @@ -0,0 +1,68 @@ +# reconstructor/weno5.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno5') +class Weno5Reconstructor(Reconstructor): + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3, v4, v5) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3, v4, v5) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 1.0/10.0 + d1 = 3.0/5.0 + d2 = 3.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = 1.0/3.0*v1-7.0/6.0*v2+11.0/6.0*v3 # r=2 + q1 = -1.0/6.0*v2+5.0/6.0*v3+1.0/3.0*v4 # r=1 + q2 = 1.0/3.0*v3+5.0/6.0*v4-1.0/6.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 3.0/10.0 + d1 = 3.0/5.0 + d2 = 1.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = -1.0/6.0*v1+5.0/6.0*v2+1.0/3.0*v3 # r=2 + q1 = 1.0/3.0*v2+5.0/6.0*v3-1.0/6.0*v4 # r=1 + q2 = 11.0/6.0*v3-7.0/6.0*v4+1.0/3.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 diff --git a/example/1d-linear-convection/weno3/python-v1/01i/residual.py b/example/1d-linear-convection/weno3/python-v1/01i/residual.py new file mode 100644 index 00000000..e0f96cd3 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux.factory import FluxCalculatorFactory +from reconstructor import ReconstructorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + + self.reconstructor = ReconstructorFactory.create(self.config, self.domain) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._compute_face_values() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _compute_face_values(self): + """私有方法:界面值重建""" + self.reconstructor.compute_face_values(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/result.py b/example/1d-linear-convection/weno3/python-v1/01i/result.py new file mode 100644 index 00000000..5e572216 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/result.py @@ -0,0 +1,25 @@ +# result.py + +class ResultAssembler: + @staticmethod + def assemble(cfd: 'Cfd') -> dict: + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied].copy() + + # ✅ 从 problem 获取解析解(唯一正确路径) + try: + analytical = cfd.problem.exact_solution(cfd) + except NotImplementedError: + analytical = None # 或 np.full_like(u_numerical, np.nan) + + return { + "x": cfd.domain.mesh.xcc, + "numerical": u_numerical, + "analytical": analytical, + "config": { + "scheme": cfd.config.recon_scheme, + "order": cfd.config.spatial_order, + "rk_order": cfd.config.rk_order, + "final_time": cfd.config.final_time, + "problem": cfd.problem.name, + } + } \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/run_eno_weno.py b/example/1d-linear-convection/weno3/python-v1/01i/run_eno_weno.py new file mode 100644 index 00000000..ea83cbe8 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/run_eno_weno.py @@ -0,0 +1,88 @@ +# run_eno_weno.py + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +from problems.linear_advection import LinearAdvectionProblem + + +def performEnoWenoAnalysisBAK(): + # 1. 初始化网格 + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行 ENO3 求解 + print("Running ENO3 solver...") + config_eno3 = CfdConfig() + config_eno3.with_reconstruction("eno", 3) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno3 = LinearAdvectionProblem(config_eno3) + cfd_eno3 = Cfd(problem_eno3, mesh) # ← 注入 problem + cfd_eno3.run() + + # 3. 配置并运行 WENO3 求解 + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno3 = LinearAdvectionProblem(config_weno3) + cfd_weno3 = Cfd(problem_weno3, mesh) # ← 注入 problem + cfd_weno3.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" + ) + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行 ENO5 求解 + print("Running ENO5 solver...") + config_eno5 = CfdConfig() + config_eno5.with_reconstruction("eno", 5) + config_eno5.dt = 0.0025 + config_eno5.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno5 = LinearAdvectionProblem(config_eno5) + cfd_eno5 = Cfd(problem_eno5, mesh) # ← 注入 problem + cfd_eno5.run() + + # 3. 配置并运行 WENO5求解 + print("Running WENO5 solver...") + config_weno5 = CfdConfig() + config_weno5.with_reconstruction("weno", 5) + config_weno5.dt = 0.0025 + config_weno5.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno5 = LinearAdvectionProblem(config_weno5) + cfd_weno5 = Cfd(problem_weno5, mesh) # ← 注入 problem + cfd_weno5.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno5.result, + weno_result=cfd_weno5.result, + save_path="eno_weno_comparison.png" + ) + + +if __name__ == "__main__": + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/solution.py b/example/1d-linear-convection/weno3/python-v1/01i/solution.py new file mode 100644 index 00000000..90666c34 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/solution.py @@ -0,0 +1,32 @@ +# solution.py +import numpy as np + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/solver.py b/example/1d-linear-convection/weno3/python-v1/01i/solver.py new file mode 100644 index 00000000..316be231 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/solver.py @@ -0,0 +1,67 @@ +# solver.py + +from mesh import Mesh +from domain import Domain +from solution import Solution +from config import CfdConfig +from result import ResultAssembler +from problem import Problem +from initial_condition import InitialConditionFactory +from boundary import BoundaryConditionFactory +from residual import ResidualCalculator +from time_integration import TimeIntegrator,TimeIntegratorFactory + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, problem: Problem, mesh): + self.problem = problem + self.config = problem.config + self.domain = Domain(problem.config, mesh) + self.solution = Solution(problem.config, self.domain) + + self.initial_condition = InitialConditionFactory.create(self.config) + self.boundary_condition = BoundaryConditionFactory.create(self) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + + # ==================== 公共接口(供 TimeIntegrator 调用) ==================== + def compute_residual(self): + """计算物理残差(封装重建→通量→散度)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件""" + self.boundary_condition.apply(self.solution.u) + + # ==================== 初始化 ==================== + def initialize(self): + """初始化全场:先 IC,再 BC,最后同步 old field""" + self.initial_condition.apply(self.solution) + # 应用边界条件到初始场 + self.apply_boundary() + # 同步 old field + self.solution.update_old_field() + + + def run(self): + self.initialize() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + self.result = ResultAssembler.assemble(self) + return self.result["numerical"] + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/time_integration/__init__.py b/example/1d-linear-convection/weno3/python-v1/01i/time_integration/__init__.py new file mode 100644 index 00000000..6342de3b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/time_integration/__init__.py @@ -0,0 +1,8 @@ +# time_integration/__init__.py + +# 导出统一接口 +from .factory import TimeIntegratorFactory +from .base import TimeIntegrator + +# 触发子模块注册(关键!) +from . import rk1, rk2, rk3 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/time_integration/base.py b/example/1d-linear-convection/weno3/python-v1/01i/time_integration/base.py new file mode 100644 index 00000000..0cdf29a4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/time_integration/base.py @@ -0,0 +1,27 @@ +# time_integration/base.py + +from abc import ABC, abstractmethod + +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + + @abstractmethod + def step(self, dt): + pass + + def compute_residual(self): + """计算残差(委托给 Cfd)""" + self.cfd.compute_residual() + + def apply_boundary(self): + """应用边界条件(委托给 Cfd)""" + self.cfd.apply_boundary() + + def map_idx(self, i): + """物理网格索引 → 残差数组索引""" + return i - self.domain.ist \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/time_integration/factory.py b/example/1d-linear-convection/weno3/python-v1/01i/time_integration/factory.py new file mode 100644 index 00000000..94662db2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/time_integration/factory.py @@ -0,0 +1,10 @@ +# time_integration/factory.py + +from core.registry import BaseFactory + +class TimeIntegratorFactory: + @staticmethod + def create(cfd) -> 'TimeIntegrator': + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/time_integration/rk1.py b/example/1d-linear-convection/weno3/python-v1/01i/time_integration/rk1.py new file mode 100644 index 00000000..b4c8a021 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/time_integration/rk1.py @@ -0,0 +1,15 @@ +# time_integration/rk1.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/time_integration/rk2.py b/example/1d-linear-convection/weno3/python-v1/01i/time_integration/rk2.py new file mode 100644 index 00000000..6d2be304 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/time_integration/rk2.py @@ -0,0 +1,29 @@ +# time_integration/rk2.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + 0.5 * self.solution.un[i] + + 0.5 * self.solution.u[i] + + 0.5 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/01i/time_integration/rk3.py b/example/1d-linear-convection/weno3/python-v1/01i/time_integration/rk3.py new file mode 100644 index 00000000..c70791e1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/01i/time_integration/rk3.py @@ -0,0 +1,43 @@ +# time_integration/rk3.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # Stage 1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # Stage 2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = ( + 0.75 * self.solution.un[i] + + 0.25 * self.solution.u[i] + + 0.25 * dt * self.solution.res[j] + ) + self.solution.u[:] = u2 + self.apply_boundary() + + # Stage 3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + c1 * self.solution.un[i] + + c2 * self.solution.u[i] + + c3 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/example/run_eno_weno.py b/example/1d-linear-convection/weno3/python-v1/02/example/run_eno_weno.py new file mode 100644 index 00000000..c4063cb7 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/example/run_eno_weno.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +# example/run_eno_weno.py + +import sys +import os +src_path = os.path.join(os.path.dirname(__file__), '..', 'src') +if src_path not in sys.path: + sys.path.insert(0, src_path) + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +from problems.linear_advection import LinearAdvectionProblem + + +def performEnoWenoAnalysisBAK(): + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + print("Running ENO3 solver...") + config_eno3 = CfdConfig() + config_eno3.with_reconstruction("eno", 3) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno3 = LinearAdvectionProblem(config_eno3) + cfd_eno3 = Cfd(problem_eno3, mesh) # ← 注入 problem + cfd_eno3.run() + + # 3. 配置并运行 WENO3 求解 + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno3 = LinearAdvectionProblem(config_weno3) + cfd_weno3 = Cfd(problem_weno3, mesh) # ← 注入 problem + cfd_weno3.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" + ) + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO5 求解 + print("Running ENO5 solver...") + config_eno5 = CfdConfig() + config_eno5.with_reconstruction("eno", 5) + config_eno5.dt = 0.0025 + config_eno5.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno5 = LinearAdvectionProblem(config_eno5) + cfd_eno5 = Cfd(problem_eno5, mesh) # ← 注入 problem + cfd_eno5.run() + + # 3. 配置并运行 WENO5求解 + print("Running WENO5 solver...") + config_weno5 = CfdConfig() + config_weno5.with_reconstruction("weno", 5) + config_weno5.dt = 0.0025 + config_weno5.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno5 = LinearAdvectionProblem(config_weno5) + cfd_weno5 = Cfd(problem_weno5, mesh) # ← 注入 problem + cfd_weno5.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno5.result, + weno_result=cfd_weno5.result, + save_path="eno_weno_comparison.png" + ) + + +if __name__ == "__main__": + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/boundary.py b/example/1d-linear-convection/weno3/python-v1/02/src/boundary.py new file mode 100644 index 00000000..2d2cfbaf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/boundary.py @@ -0,0 +1,104 @@ +from abc import ABC, abstractmethod +from core.registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from core.registry import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/config.py b/example/1d-linear-convection/weno3/python-v1/02/src/config.py new file mode 100644 index 00000000..6432c80e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/config.py @@ -0,0 +1,45 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + #self.ic_type = "sin" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # "rusanov", "engquist-osher" + #self.flux_type = "engquist-osher" # "rusanov", "engquist-osher" + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + print(f"scheme={scheme}") + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/core/registry.py b/example/1d-linear-convection/weno3/python-v1/02/src/core/registry.py new file mode 100644 index 00000000..4a52afc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/core/registry.py @@ -0,0 +1,83 @@ +# core/registry.py +""" +统一注册系统 + 通用工厂 +- ComponentRegistry: 组件注册表 +- register_component: 装饰器 +- BaseFactory: 通用工厂类 +""" + +from typing import Dict, Type, Any + + +# ==================== 1. 注册表核心 ==================== +class ComponentRegistry: + """组件注册表""" + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True + + @classmethod + def set_verbose(cls, verbose: bool): + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + #print(f"ComponentRegistry register cls={cls}") + #print(f"ComponentRegistry register name={name}") + #print(f"ComponentRegistry register component_class={component_class}") + if category not in cls._registries: + cls._registries[category] = {} + if name in cls._registries[category]: + if cls._registries[category][name] != component_class and cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + if category not in cls._registries: + raise ValueError(f"❌ 未知类别: {category} (可用: {list(cls._registries.keys())})") + if name not in cls._registries[category]: + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {list(cls._registries[category].keys())})") + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) for cat, comps in cls._registries.items()} + + +# ==================== 2. 装饰器 ==================== +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + #print(f"register_component decorator name={name}") + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + +# ==================== 3. 通用工厂 ==================== +class BaseFactory: + """通用工厂基类""" + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + name_lower = name.lower() + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + available = ComponentRegistry.list_all().get(category, []) + if available: + error_msg = f"不支持的 {category} 类型 '{name}'。可用类型:{available}" + else: + error_msg = f"不支持的 {category} 类型 '{name}'(无已注册组件)" + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/domain.py b/example/1d-linear-convection/weno3/python-v1/02/src/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/equations/base.py b/example/1d-linear-convection/weno3/python-v1/02/src/equations/base.py new file mode 100644 index 00000000..92e54f12 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/equations/base.py @@ -0,0 +1,56 @@ +# equations/base.py + +from abc import ABC, abstractmethod +import numpy as np + +class Equation(ABC): + """ + 控制方程抽象基类 + 所有物理方程(线性对流、Euler、MHD)必须继承此类 + """ + + @property + @abstractmethod + def num_equations(self) -> int: + """返回方程组变量数(标量=1,Euler=3,MHD=8)""" + pass + + @abstractmethod + def flux(self, u): + """ + 计算通量函数 f(u) + :param u: 状态向量 (num_equations,) + :return: 通量向量 (num_equations,) + """ + pass + + @abstractmethod + def max_wave_speed(self, u): + """ + 计算最大波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + @abstractmethod + def wave_speed(self): + """ + 计算波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + def exact_solution(self, x, t, initial_condition_func): + """ + 可选:提供解析解 + 子类可重写;若无解析解,调用方应捕获 NotImplementedError + :param x: 空间坐标数组 (ncells,) + :param t: 时间 + :param initial_condition_func: 初值函数 u0(x) -> (num_equations, ncells) + :return: 解 u(x,t) -> (num_equations, ncells) + """ + raise NotImplementedError( + f"Equation '{self.__class__.__name__}' does not provide an exact solution." + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/equations/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/02/src/equations/linear_advection.py new file mode 100644 index 00000000..26aa026b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/equations/linear_advection.py @@ -0,0 +1,42 @@ +# equations/linear_advection.py + +import numpy as np +from .base import Equation + +class LinearAdvectionEquation(Equation): + """ + 线性对流方程: u_t + c * u_x = 0 + 支持标量(num_equations=1) + """ + + def __init__(self, wave_speed: float): + self.c = wave_speed + + @property + def num_equations(self) -> int: + return 1 + + def flux(self, u): + """f(u) = c * u""" + return self.c * u + + def max_wave_speed(self, u): + """最大波速 = |c|""" + return abs(self.c) + + def wave_speed(self): + return self.c + + + def exact_solution(self, x, t, initial_condition_func): + """ + 解析解: u(x, t) = u0(x - c * t) + 支持周期边界 + """ + if len(x) == 0: + return np.zeros((1, 0)) + + L = x[-1] - x[0] # 假设均匀周期网格 + x_shifted = (x - self.c * t + L) % L + u0 = initial_condition_func(x_shifted) # (1, ncells) + return u0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/flux/__init__.py b/example/1d-linear-convection/weno3/python-v1/02/src/flux/__init__.py new file mode 100644 index 00000000..432a36cb --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/flux/__init__.py @@ -0,0 +1,7 @@ +# flux/__init__.py +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 确保子模块被导入以触发注册 +from . import rusanov +from . import engquist_osher \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/flux/base.py b/example/1d-linear-convection/weno3/python-v1/02/src/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python-v1/02/src/flux/engquist_osher.py new file mode 100644 index 00000000..eb7b8cdf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/flux/engquist_osher.py @@ -0,0 +1,20 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + eq = self.cfd.problem.equation + for i in range(self.mesh.nnodes): + c = eq.wave_speed() + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/flux/factory.py b/example/1d-linear-convection/weno3/python-v1/02/src/flux/factory.py new file mode 100644 index 00000000..5ae77c3f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/flux/factory.py @@ -0,0 +1,25 @@ +# flux/factory.py +""" +通量计算器专用工厂(封装注册细节,提供清晰接口) +符合你希望“将创建逻辑封装在工厂中”的设计原则 +""" + +from core.registry import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/flux/rusanov.py b/example/1d-linear-convection/weno3/python-v1/02/src/flux/rusanov.py new file mode 100644 index 00000000..baa7c62c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/flux/rusanov.py @@ -0,0 +1,25 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量,使用 Equation 解耦物理参数""" + + def compute(self, q_face_left, q_face_right, flux): + # 从 cfd 获取 equation(与 Julia 对齐) + eq = self.cfd.problem.equation + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = eq.wave_speed() + c_R = eq.wave_speed() + # 通过 equation 计算通量和波速 + F_L = eq.flux(u_L) + F_R = eq.flux(u_R) + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/initial_condition.py b/example/1d-linear-convection/weno3/python-v1/02/src/initial_condition.py new file mode 100644 index 00000000..dda8a3bd --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/initial_condition.py @@ -0,0 +1,90 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from core.registry import ComponentRegistry, register_component + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from core.registry import BaseFactory + return BaseFactory.create_component('initial_condition', config.ic_type, config) + diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/mesh.py b/example/1d-linear-convection/weno3/python-v1/02/src/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/plotter.py b/example/1d-linear-convection/weno3/python-v1/02/src/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/problem.py b/example/1d-linear-convection/weno3/python-v1/02/src/problem.py new file mode 100644 index 00000000..53719f26 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/problem.py @@ -0,0 +1,38 @@ +# problem.py +""" +问题定义模块:每个 Problem 子类代表一个完整测试用例 +包含初始条件、解析解(可选)、物理方程等 +""" + +from abc import ABC, abstractmethod +from initial_condition import InitialConditionFactory + + +class Problem(ABC): + """ + 抽象问题基类 + 每个具体问题(如线性对流、Sod 激波管)应继承此类 + """ + def __init__(self, config): + self.config = config + + @abstractmethod + def initial_condition(self): + """ + 返回 InitialCondition 实例 + """ + pass + + def exact_solution(self, cfd): + """ + 可选:返回解析解(数值数组) + 若无解析解,可抛出 NotImplementedError 或返回 None + """ + x = cfd.domain.mesh.xcc + raise NotImplementedError( + f"Problem '{self.__class__.__name__}' does not provide an exact solution." + ) + + @property + def name(self): + return self.__class__.__name__ \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/problems/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/02/src/problems/linear_advection.py new file mode 100644 index 00000000..b6d548f5 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/problems/linear_advection.py @@ -0,0 +1,39 @@ +# problems/linear_advection.py + +import numpy as np +from problem import Problem +from equations.linear_advection import LinearAdvectionEquation +from initial_condition import InitialConditionFactory + +class LinearAdvectionProblem(Problem): + """ + 线性对流问题:u_t + c u_x = 0 + 使用周期边界 + 任意初始条件 + """ + + def __init__(self, config): + super().__init__(config) + # 持有物理方程实例 + self.equation = LinearAdvectionEquation(wave_speed=config.wave_speed) + self._ic_cache = None # 缓存 IC 实例 + + def initial_condition(self): + """返回初始条件实例""" + if self._ic_cache is None: + self._ic_cache = InitialConditionFactory.create(self.config) + return self._ic_cache + + def exact_solution(self, cfd): + """ + 委托给 Equation 计算解析解 + """ + ic = self.initial_condition() + # 包装 evaluate_at 以返回 (1, ncells) + def vectorized_ic(x): + u0_scalar = ic.evaluate_at(x) # (ncells,) + return u0_scalar[np.newaxis, :] # (1, ncells) + + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + u_exact = self.equation.exact_solution(x, t, vectorized_ic) + return u_exact[0] # 返回标量数组 (ncells,) 以兼容现有绘图逻辑 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python-v1/02/src/reconstructor/__init__.py new file mode 100644 index 00000000..d705bb98 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/reconstructor/__init__.py @@ -0,0 +1,6 @@ +# reconstructor/__init__.py +from .base import Reconstructor +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from .weno5 import Weno5Reconstructor +from .factory import ReconstructorFactory diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/reconstructor/base.py b/example/1d-linear-convection/weno3/python-v1/02/src/reconstructor/base.py new file mode 100644 index 00000000..094b4712 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def compute_face_values(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/reconstructor/eno.py b/example/1d-linear-convection/weno3/python-v1/02/src/reconstructor/eno.py new file mode 100644 index 00000000..4c8fc1e4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def compute_face_values(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/reconstructor/factory.py b/example/1d-linear-convection/weno3/python-v1/02/src/reconstructor/factory.py new file mode 100644 index 00000000..25e755e0 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/reconstructor/factory.py @@ -0,0 +1,28 @@ +# reconstructor/factory.py +from core.registry import BaseFactory + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + print(f"ReconstructorFactory scheme={scheme}") + + print(f"scheme={scheme}") + if scheme == "eno": + if order is None: + order = 3 + return BaseFactory.create_component('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3" or scheme == "weno5": + # WENO3,WENO5无参数 + print(f"scheme == 'eno3' or scheme == 'weno5' scheme={scheme}") + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python-v1/02/src/reconstructor/weno3.py new file mode 100644 index 00000000..cc7d82df --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/reconstructor/weno3.py @@ -0,0 +1,59 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/reconstructor/weno5.py b/example/1d-linear-convection/weno3/python-v1/02/src/reconstructor/weno5.py new file mode 100644 index 00000000..af5ca809 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/reconstructor/weno5.py @@ -0,0 +1,68 @@ +# reconstructor/weno5.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno5') +class Weno5Reconstructor(Reconstructor): + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3, v4, v5) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3, v4, v5) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 1.0/10.0 + d1 = 3.0/5.0 + d2 = 3.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = 1.0/3.0*v1-7.0/6.0*v2+11.0/6.0*v3 # r=2 + q1 = -1.0/6.0*v2+5.0/6.0*v3+1.0/3.0*v4 # r=1 + q2 = 1.0/3.0*v3+5.0/6.0*v4-1.0/6.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 3.0/10.0 + d1 = 3.0/5.0 + d2 = 1.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = -1.0/6.0*v1+5.0/6.0*v2+1.0/3.0*v3 # r=2 + q1 = 1.0/3.0*v2+5.0/6.0*v3-1.0/6.0*v4 # r=1 + q2 = 11.0/6.0*v3-7.0/6.0*v4+1.0/3.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/residual.py b/example/1d-linear-convection/weno3/python-v1/02/src/residual.py new file mode 100644 index 00000000..e0f96cd3 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux.factory import FluxCalculatorFactory +from reconstructor import ReconstructorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + + self.reconstructor = ReconstructorFactory.create(self.config, self.domain) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._compute_face_values() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _compute_face_values(self): + """私有方法:界面值重建""" + self.reconstructor.compute_face_values(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/result.py b/example/1d-linear-convection/weno3/python-v1/02/src/result.py new file mode 100644 index 00000000..5e572216 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/result.py @@ -0,0 +1,25 @@ +# result.py + +class ResultAssembler: + @staticmethod + def assemble(cfd: 'Cfd') -> dict: + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied].copy() + + # ✅ 从 problem 获取解析解(唯一正确路径) + try: + analytical = cfd.problem.exact_solution(cfd) + except NotImplementedError: + analytical = None # 或 np.full_like(u_numerical, np.nan) + + return { + "x": cfd.domain.mesh.xcc, + "numerical": u_numerical, + "analytical": analytical, + "config": { + "scheme": cfd.config.recon_scheme, + "order": cfd.config.spatial_order, + "rk_order": cfd.config.rk_order, + "final_time": cfd.config.final_time, + "problem": cfd.problem.name, + } + } \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/solution.py b/example/1d-linear-convection/weno3/python-v1/02/src/solution.py new file mode 100644 index 00000000..90666c34 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/solution.py @@ -0,0 +1,32 @@ +# solution.py +import numpy as np + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/solver.py b/example/1d-linear-convection/weno3/python-v1/02/src/solver.py new file mode 100644 index 00000000..316be231 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/solver.py @@ -0,0 +1,67 @@ +# solver.py + +from mesh import Mesh +from domain import Domain +from solution import Solution +from config import CfdConfig +from result import ResultAssembler +from problem import Problem +from initial_condition import InitialConditionFactory +from boundary import BoundaryConditionFactory +from residual import ResidualCalculator +from time_integration import TimeIntegrator,TimeIntegratorFactory + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, problem: Problem, mesh): + self.problem = problem + self.config = problem.config + self.domain = Domain(problem.config, mesh) + self.solution = Solution(problem.config, self.domain) + + self.initial_condition = InitialConditionFactory.create(self.config) + self.boundary_condition = BoundaryConditionFactory.create(self) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + + # ==================== 公共接口(供 TimeIntegrator 调用) ==================== + def compute_residual(self): + """计算物理残差(封装重建→通量→散度)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件""" + self.boundary_condition.apply(self.solution.u) + + # ==================== 初始化 ==================== + def initialize(self): + """初始化全场:先 IC,再 BC,最后同步 old field""" + self.initial_condition.apply(self.solution) + # 应用边界条件到初始场 + self.apply_boundary() + # 同步 old field + self.solution.update_old_field() + + + def run(self): + self.initialize() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + self.result = ResultAssembler.assemble(self) + return self.result["numerical"] + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/time_integration/__init__.py b/example/1d-linear-convection/weno3/python-v1/02/src/time_integration/__init__.py new file mode 100644 index 00000000..6342de3b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/time_integration/__init__.py @@ -0,0 +1,8 @@ +# time_integration/__init__.py + +# 导出统一接口 +from .factory import TimeIntegratorFactory +from .base import TimeIntegrator + +# 触发子模块注册(关键!) +from . import rk1, rk2, rk3 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/time_integration/base.py b/example/1d-linear-convection/weno3/python-v1/02/src/time_integration/base.py new file mode 100644 index 00000000..0cdf29a4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/time_integration/base.py @@ -0,0 +1,27 @@ +# time_integration/base.py + +from abc import ABC, abstractmethod + +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + + @abstractmethod + def step(self, dt): + pass + + def compute_residual(self): + """计算残差(委托给 Cfd)""" + self.cfd.compute_residual() + + def apply_boundary(self): + """应用边界条件(委托给 Cfd)""" + self.cfd.apply_boundary() + + def map_idx(self, i): + """物理网格索引 → 残差数组索引""" + return i - self.domain.ist \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/time_integration/factory.py b/example/1d-linear-convection/weno3/python-v1/02/src/time_integration/factory.py new file mode 100644 index 00000000..94662db2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/time_integration/factory.py @@ -0,0 +1,10 @@ +# time_integration/factory.py + +from core.registry import BaseFactory + +class TimeIntegratorFactory: + @staticmethod + def create(cfd) -> 'TimeIntegrator': + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/time_integration/rk1.py b/example/1d-linear-convection/weno3/python-v1/02/src/time_integration/rk1.py new file mode 100644 index 00000000..b4c8a021 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/time_integration/rk1.py @@ -0,0 +1,15 @@ +# time_integration/rk1.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/time_integration/rk2.py b/example/1d-linear-convection/weno3/python-v1/02/src/time_integration/rk2.py new file mode 100644 index 00000000..6d2be304 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/time_integration/rk2.py @@ -0,0 +1,29 @@ +# time_integration/rk2.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + 0.5 * self.solution.un[i] + + 0.5 * self.solution.u[i] + + 0.5 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02/src/time_integration/rk3.py b/example/1d-linear-convection/weno3/python-v1/02/src/time_integration/rk3.py new file mode 100644 index 00000000..c70791e1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02/src/time_integration/rk3.py @@ -0,0 +1,43 @@ +# time_integration/rk3.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # Stage 1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # Stage 2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = ( + 0.75 * self.solution.un[i] + + 0.25 * self.solution.u[i] + + 0.25 * dt * self.solution.res[j] + ) + self.solution.u[:] = u2 + self.apply_boundary() + + # Stage 3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + c1 * self.solution.un[i] + + c2 * self.solution.u[i] + + c3 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/example/run_eno_weno.py b/example/1d-linear-convection/weno3/python-v1/02a/example/run_eno_weno.py new file mode 100644 index 00000000..c4063cb7 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/example/run_eno_weno.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +# example/run_eno_weno.py + +import sys +import os +src_path = os.path.join(os.path.dirname(__file__), '..', 'src') +if src_path not in sys.path: + sys.path.insert(0, src_path) + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +from problems.linear_advection import LinearAdvectionProblem + + +def performEnoWenoAnalysisBAK(): + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + print("Running ENO3 solver...") + config_eno3 = CfdConfig() + config_eno3.with_reconstruction("eno", 3) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno3 = LinearAdvectionProblem(config_eno3) + cfd_eno3 = Cfd(problem_eno3, mesh) # ← 注入 problem + cfd_eno3.run() + + # 3. 配置并运行 WENO3 求解 + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno3 = LinearAdvectionProblem(config_weno3) + cfd_weno3 = Cfd(problem_weno3, mesh) # ← 注入 problem + cfd_weno3.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" + ) + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO5 求解 + print("Running ENO5 solver...") + config_eno5 = CfdConfig() + config_eno5.with_reconstruction("eno", 5) + config_eno5.dt = 0.0025 + config_eno5.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno5 = LinearAdvectionProblem(config_eno5) + cfd_eno5 = Cfd(problem_eno5, mesh) # ← 注入 problem + cfd_eno5.run() + + # 3. 配置并运行 WENO5求解 + print("Running WENO5 solver...") + config_weno5 = CfdConfig() + config_weno5.with_reconstruction("weno", 5) + config_weno5.dt = 0.0025 + config_weno5.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno5 = LinearAdvectionProblem(config_weno5) + cfd_weno5 = Cfd(problem_weno5, mesh) # ← 注入 problem + cfd_weno5.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno5.result, + weno_result=cfd_weno5.result, + save_path="eno_weno_comparison.png" + ) + + +if __name__ == "__main__": + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/boundary.py b/example/1d-linear-convection/weno3/python-v1/02a/src/boundary.py new file mode 100644 index 00000000..2d2cfbaf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/boundary.py @@ -0,0 +1,104 @@ +from abc import ABC, abstractmethod +from core.registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from core.registry import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/config.py b/example/1d-linear-convection/weno3/python-v1/02a/src/config.py new file mode 100644 index 00000000..6432c80e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/config.py @@ -0,0 +1,45 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + #self.ic_type = "sin" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # "rusanov", "engquist-osher" + #self.flux_type = "engquist-osher" # "rusanov", "engquist-osher" + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + print(f"scheme={scheme}") + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/core/registry.py b/example/1d-linear-convection/weno3/python-v1/02a/src/core/registry.py new file mode 100644 index 00000000..4a52afc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/core/registry.py @@ -0,0 +1,83 @@ +# core/registry.py +""" +统一注册系统 + 通用工厂 +- ComponentRegistry: 组件注册表 +- register_component: 装饰器 +- BaseFactory: 通用工厂类 +""" + +from typing import Dict, Type, Any + + +# ==================== 1. 注册表核心 ==================== +class ComponentRegistry: + """组件注册表""" + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True + + @classmethod + def set_verbose(cls, verbose: bool): + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + #print(f"ComponentRegistry register cls={cls}") + #print(f"ComponentRegistry register name={name}") + #print(f"ComponentRegistry register component_class={component_class}") + if category not in cls._registries: + cls._registries[category] = {} + if name in cls._registries[category]: + if cls._registries[category][name] != component_class and cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + if category not in cls._registries: + raise ValueError(f"❌ 未知类别: {category} (可用: {list(cls._registries.keys())})") + if name not in cls._registries[category]: + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {list(cls._registries[category].keys())})") + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) for cat, comps in cls._registries.items()} + + +# ==================== 2. 装饰器 ==================== +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + #print(f"register_component decorator name={name}") + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + +# ==================== 3. 通用工厂 ==================== +class BaseFactory: + """通用工厂基类""" + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + name_lower = name.lower() + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + available = ComponentRegistry.list_all().get(category, []) + if available: + error_msg = f"不支持的 {category} 类型 '{name}'。可用类型:{available}" + else: + error_msg = f"不支持的 {category} 类型 '{name}'(无已注册组件)" + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/domain.py b/example/1d-linear-convection/weno3/python-v1/02a/src/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/equations/base.py b/example/1d-linear-convection/weno3/python-v1/02a/src/equations/base.py new file mode 100644 index 00000000..92e54f12 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/equations/base.py @@ -0,0 +1,56 @@ +# equations/base.py + +from abc import ABC, abstractmethod +import numpy as np + +class Equation(ABC): + """ + 控制方程抽象基类 + 所有物理方程(线性对流、Euler、MHD)必须继承此类 + """ + + @property + @abstractmethod + def num_equations(self) -> int: + """返回方程组变量数(标量=1,Euler=3,MHD=8)""" + pass + + @abstractmethod + def flux(self, u): + """ + 计算通量函数 f(u) + :param u: 状态向量 (num_equations,) + :return: 通量向量 (num_equations,) + """ + pass + + @abstractmethod + def max_wave_speed(self, u): + """ + 计算最大波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + @abstractmethod + def wave_speed(self): + """ + 计算波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + def exact_solution(self, x, t, initial_condition_func): + """ + 可选:提供解析解 + 子类可重写;若无解析解,调用方应捕获 NotImplementedError + :param x: 空间坐标数组 (ncells,) + :param t: 时间 + :param initial_condition_func: 初值函数 u0(x) -> (num_equations, ncells) + :return: 解 u(x,t) -> (num_equations, ncells) + """ + raise NotImplementedError( + f"Equation '{self.__class__.__name__}' does not provide an exact solution." + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/equations/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/02a/src/equations/linear_advection.py new file mode 100644 index 00000000..26aa026b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/equations/linear_advection.py @@ -0,0 +1,42 @@ +# equations/linear_advection.py + +import numpy as np +from .base import Equation + +class LinearAdvectionEquation(Equation): + """ + 线性对流方程: u_t + c * u_x = 0 + 支持标量(num_equations=1) + """ + + def __init__(self, wave_speed: float): + self.c = wave_speed + + @property + def num_equations(self) -> int: + return 1 + + def flux(self, u): + """f(u) = c * u""" + return self.c * u + + def max_wave_speed(self, u): + """最大波速 = |c|""" + return abs(self.c) + + def wave_speed(self): + return self.c + + + def exact_solution(self, x, t, initial_condition_func): + """ + 解析解: u(x, t) = u0(x - c * t) + 支持周期边界 + """ + if len(x) == 0: + return np.zeros((1, 0)) + + L = x[-1] - x[0] # 假设均匀周期网格 + x_shifted = (x - self.c * t + L) % L + u0 = initial_condition_func(x_shifted) # (1, ncells) + return u0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/flux/__init__.py b/example/1d-linear-convection/weno3/python-v1/02a/src/flux/__init__.py new file mode 100644 index 00000000..432a36cb --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/flux/__init__.py @@ -0,0 +1,7 @@ +# flux/__init__.py +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 确保子模块被导入以触发注册 +from . import rusanov +from . import engquist_osher \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/flux/base.py b/example/1d-linear-convection/weno3/python-v1/02a/src/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python-v1/02a/src/flux/engquist_osher.py new file mode 100644 index 00000000..eb7b8cdf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/flux/engquist_osher.py @@ -0,0 +1,20 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + eq = self.cfd.problem.equation + for i in range(self.mesh.nnodes): + c = eq.wave_speed() + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/flux/factory.py b/example/1d-linear-convection/weno3/python-v1/02a/src/flux/factory.py new file mode 100644 index 00000000..5ae77c3f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/flux/factory.py @@ -0,0 +1,25 @@ +# flux/factory.py +""" +通量计算器专用工厂(封装注册细节,提供清晰接口) +符合你希望“将创建逻辑封装在工厂中”的设计原则 +""" + +from core.registry import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/flux/rusanov.py b/example/1d-linear-convection/weno3/python-v1/02a/src/flux/rusanov.py new file mode 100644 index 00000000..baa7c62c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/flux/rusanov.py @@ -0,0 +1,25 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量,使用 Equation 解耦物理参数""" + + def compute(self, q_face_left, q_face_right, flux): + # 从 cfd 获取 equation(与 Julia 对齐) + eq = self.cfd.problem.equation + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = eq.wave_speed() + c_R = eq.wave_speed() + # 通过 equation 计算通量和波速 + F_L = eq.flux(u_L) + F_R = eq.flux(u_R) + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/initial_condition.py b/example/1d-linear-convection/weno3/python-v1/02a/src/initial_condition.py new file mode 100644 index 00000000..dda8a3bd --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/initial_condition.py @@ -0,0 +1,90 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from core.registry import ComponentRegistry, register_component + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from core.registry import BaseFactory + return BaseFactory.create_component('initial_condition', config.ic_type, config) + diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/mesh.py b/example/1d-linear-convection/weno3/python-v1/02a/src/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/plotter.py b/example/1d-linear-convection/weno3/python-v1/02a/src/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/problem.py b/example/1d-linear-convection/weno3/python-v1/02a/src/problem.py new file mode 100644 index 00000000..53719f26 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/problem.py @@ -0,0 +1,38 @@ +# problem.py +""" +问题定义模块:每个 Problem 子类代表一个完整测试用例 +包含初始条件、解析解(可选)、物理方程等 +""" + +from abc import ABC, abstractmethod +from initial_condition import InitialConditionFactory + + +class Problem(ABC): + """ + 抽象问题基类 + 每个具体问题(如线性对流、Sod 激波管)应继承此类 + """ + def __init__(self, config): + self.config = config + + @abstractmethod + def initial_condition(self): + """ + 返回 InitialCondition 实例 + """ + pass + + def exact_solution(self, cfd): + """ + 可选:返回解析解(数值数组) + 若无解析解,可抛出 NotImplementedError 或返回 None + """ + x = cfd.domain.mesh.xcc + raise NotImplementedError( + f"Problem '{self.__class__.__name__}' does not provide an exact solution." + ) + + @property + def name(self): + return self.__class__.__name__ \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/problems/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/02a/src/problems/linear_advection.py new file mode 100644 index 00000000..b6d548f5 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/problems/linear_advection.py @@ -0,0 +1,39 @@ +# problems/linear_advection.py + +import numpy as np +from problem import Problem +from equations.linear_advection import LinearAdvectionEquation +from initial_condition import InitialConditionFactory + +class LinearAdvectionProblem(Problem): + """ + 线性对流问题:u_t + c u_x = 0 + 使用周期边界 + 任意初始条件 + """ + + def __init__(self, config): + super().__init__(config) + # 持有物理方程实例 + self.equation = LinearAdvectionEquation(wave_speed=config.wave_speed) + self._ic_cache = None # 缓存 IC 实例 + + def initial_condition(self): + """返回初始条件实例""" + if self._ic_cache is None: + self._ic_cache = InitialConditionFactory.create(self.config) + return self._ic_cache + + def exact_solution(self, cfd): + """ + 委托给 Equation 计算解析解 + """ + ic = self.initial_condition() + # 包装 evaluate_at 以返回 (1, ncells) + def vectorized_ic(x): + u0_scalar = ic.evaluate_at(x) # (ncells,) + return u0_scalar[np.newaxis, :] # (1, ncells) + + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + u_exact = self.equation.exact_solution(x, t, vectorized_ic) + return u_exact[0] # 返回标量数组 (ncells,) 以兼容现有绘图逻辑 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python-v1/02a/src/reconstructor/__init__.py new file mode 100644 index 00000000..d705bb98 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/reconstructor/__init__.py @@ -0,0 +1,6 @@ +# reconstructor/__init__.py +from .base import Reconstructor +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from .weno5 import Weno5Reconstructor +from .factory import ReconstructorFactory diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/reconstructor/base.py b/example/1d-linear-convection/weno3/python-v1/02a/src/reconstructor/base.py new file mode 100644 index 00000000..de00327c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def compute_face_values(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/reconstructor/eno.py b/example/1d-linear-convection/weno3/python-v1/02a/src/reconstructor/eno.py new file mode 100644 index 00000000..60699adf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/reconstructor/eno.py @@ -0,0 +1,96 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.initialize(self.config.spatial_order, self.domain.ntcells) + + def initialize(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def compute_face_values(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/reconstructor/factory.py b/example/1d-linear-convection/weno3/python-v1/02a/src/reconstructor/factory.py new file mode 100644 index 00000000..e287ab84 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/reconstructor/factory.py @@ -0,0 +1,14 @@ +# reconstructor/factory.py +from core.registry import BaseFactory + +class ReconstructorFactory: + @staticmethod + def create(cfd): + config = cfd.config + scheme = config.recon_scheme.lower() + + if scheme.startswith("weno"): + if scheme == "weno": + order = getattr(config, 'spatial_order', None) + scheme = f"weno{order}" + return BaseFactory.create_component('reconstructor', scheme, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python-v1/02a/src/reconstructor/weno3.py new file mode 100644 index 00000000..f45eb35f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/reconstructor/weno3.py @@ -0,0 +1,62 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def __init__(self, cfd): + self.cfd = cfd + + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/reconstructor/weno5.py b/example/1d-linear-convection/weno3/python-v1/02a/src/reconstructor/weno5.py new file mode 100644 index 00000000..9d665275 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/reconstructor/weno5.py @@ -0,0 +1,71 @@ +# reconstructor/weno5.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno5') +class Weno5Reconstructor(Reconstructor): + def __init__(self, cfd): + self.cfd = cfd + + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3, v4, v5) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3, v4, v5) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 1.0/10.0 + d1 = 3.0/5.0 + d2 = 3.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = 1.0/3.0*v1-7.0/6.0*v2+11.0/6.0*v3 # r=2 + q1 = -1.0/6.0*v2+5.0/6.0*v3+1.0/3.0*v4 # r=1 + q2 = 1.0/3.0*v3+5.0/6.0*v4-1.0/6.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 3.0/10.0 + d1 = 3.0/5.0 + d2 = 1.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = -1.0/6.0*v1+5.0/6.0*v2+1.0/3.0*v3 # r=2 + q1 = 1.0/3.0*v2+5.0/6.0*v3-1.0/6.0*v4 # r=1 + q2 = 11.0/6.0*v3-7.0/6.0*v4+1.0/3.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/residual.py b/example/1d-linear-convection/weno3/python-v1/02a/src/residual.py new file mode 100644 index 00000000..27d5a0c4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/residual.py @@ -0,0 +1,41 @@ +# residual.py + +from flux.factory import FluxCalculatorFactory +from reconstructor import ReconstructorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + + #self.reconstructor = ReconstructorFactory.create(self.config, self.domain) + self.reconstructor = ReconstructorFactory.create(cfd) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._compute_face_values() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _compute_face_values(self): + """私有方法:界面值重建""" + self.reconstructor.compute_face_values(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/result.py b/example/1d-linear-convection/weno3/python-v1/02a/src/result.py new file mode 100644 index 00000000..5e572216 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/result.py @@ -0,0 +1,25 @@ +# result.py + +class ResultAssembler: + @staticmethod + def assemble(cfd: 'Cfd') -> dict: + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied].copy() + + # ✅ 从 problem 获取解析解(唯一正确路径) + try: + analytical = cfd.problem.exact_solution(cfd) + except NotImplementedError: + analytical = None # 或 np.full_like(u_numerical, np.nan) + + return { + "x": cfd.domain.mesh.xcc, + "numerical": u_numerical, + "analytical": analytical, + "config": { + "scheme": cfd.config.recon_scheme, + "order": cfd.config.spatial_order, + "rk_order": cfd.config.rk_order, + "final_time": cfd.config.final_time, + "problem": cfd.problem.name, + } + } \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/solution.py b/example/1d-linear-convection/weno3/python-v1/02a/src/solution.py new file mode 100644 index 00000000..90666c34 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/solution.py @@ -0,0 +1,32 @@ +# solution.py +import numpy as np + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/solver.py b/example/1d-linear-convection/weno3/python-v1/02a/src/solver.py new file mode 100644 index 00000000..316be231 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/solver.py @@ -0,0 +1,67 @@ +# solver.py + +from mesh import Mesh +from domain import Domain +from solution import Solution +from config import CfdConfig +from result import ResultAssembler +from problem import Problem +from initial_condition import InitialConditionFactory +from boundary import BoundaryConditionFactory +from residual import ResidualCalculator +from time_integration import TimeIntegrator,TimeIntegratorFactory + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, problem: Problem, mesh): + self.problem = problem + self.config = problem.config + self.domain = Domain(problem.config, mesh) + self.solution = Solution(problem.config, self.domain) + + self.initial_condition = InitialConditionFactory.create(self.config) + self.boundary_condition = BoundaryConditionFactory.create(self) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + + # ==================== 公共接口(供 TimeIntegrator 调用) ==================== + def compute_residual(self): + """计算物理残差(封装重建→通量→散度)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件""" + self.boundary_condition.apply(self.solution.u) + + # ==================== 初始化 ==================== + def initialize(self): + """初始化全场:先 IC,再 BC,最后同步 old field""" + self.initial_condition.apply(self.solution) + # 应用边界条件到初始场 + self.apply_boundary() + # 同步 old field + self.solution.update_old_field() + + + def run(self): + self.initialize() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + self.result = ResultAssembler.assemble(self) + return self.result["numerical"] + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/time_integration/__init__.py b/example/1d-linear-convection/weno3/python-v1/02a/src/time_integration/__init__.py new file mode 100644 index 00000000..6342de3b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/time_integration/__init__.py @@ -0,0 +1,8 @@ +# time_integration/__init__.py + +# 导出统一接口 +from .factory import TimeIntegratorFactory +from .base import TimeIntegrator + +# 触发子模块注册(关键!) +from . import rk1, rk2, rk3 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/time_integration/base.py b/example/1d-linear-convection/weno3/python-v1/02a/src/time_integration/base.py new file mode 100644 index 00000000..0cdf29a4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/time_integration/base.py @@ -0,0 +1,27 @@ +# time_integration/base.py + +from abc import ABC, abstractmethod + +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + + @abstractmethod + def step(self, dt): + pass + + def compute_residual(self): + """计算残差(委托给 Cfd)""" + self.cfd.compute_residual() + + def apply_boundary(self): + """应用边界条件(委托给 Cfd)""" + self.cfd.apply_boundary() + + def map_idx(self, i): + """物理网格索引 → 残差数组索引""" + return i - self.domain.ist \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/time_integration/factory.py b/example/1d-linear-convection/weno3/python-v1/02a/src/time_integration/factory.py new file mode 100644 index 00000000..94662db2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/time_integration/factory.py @@ -0,0 +1,10 @@ +# time_integration/factory.py + +from core.registry import BaseFactory + +class TimeIntegratorFactory: + @staticmethod + def create(cfd) -> 'TimeIntegrator': + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/time_integration/rk1.py b/example/1d-linear-convection/weno3/python-v1/02a/src/time_integration/rk1.py new file mode 100644 index 00000000..b4c8a021 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/time_integration/rk1.py @@ -0,0 +1,15 @@ +# time_integration/rk1.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/time_integration/rk2.py b/example/1d-linear-convection/weno3/python-v1/02a/src/time_integration/rk2.py new file mode 100644 index 00000000..6d2be304 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/time_integration/rk2.py @@ -0,0 +1,29 @@ +# time_integration/rk2.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + 0.5 * self.solution.un[i] + + 0.5 * self.solution.u[i] + + 0.5 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02a/src/time_integration/rk3.py b/example/1d-linear-convection/weno3/python-v1/02a/src/time_integration/rk3.py new file mode 100644 index 00000000..c70791e1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02a/src/time_integration/rk3.py @@ -0,0 +1,43 @@ +# time_integration/rk3.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # Stage 1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # Stage 2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = ( + 0.75 * self.solution.un[i] + + 0.25 * self.solution.u[i] + + 0.25 * dt * self.solution.res[j] + ) + self.solution.u[:] = u2 + self.apply_boundary() + + # Stage 3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + c1 * self.solution.un[i] + + c2 * self.solution.u[i] + + c3 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/example/run_eno_weno.py b/example/1d-linear-convection/weno3/python-v1/02b/example/run_eno_weno.py new file mode 100644 index 00000000..c4063cb7 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/example/run_eno_weno.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +# example/run_eno_weno.py + +import sys +import os +src_path = os.path.join(os.path.dirname(__file__), '..', 'src') +if src_path not in sys.path: + sys.path.insert(0, src_path) + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +from problems.linear_advection import LinearAdvectionProblem + + +def performEnoWenoAnalysisBAK(): + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + print("Running ENO3 solver...") + config_eno3 = CfdConfig() + config_eno3.with_reconstruction("eno", 3) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno3 = LinearAdvectionProblem(config_eno3) + cfd_eno3 = Cfd(problem_eno3, mesh) # ← 注入 problem + cfd_eno3.run() + + # 3. 配置并运行 WENO3 求解 + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno3 = LinearAdvectionProblem(config_weno3) + cfd_weno3 = Cfd(problem_weno3, mesh) # ← 注入 problem + cfd_weno3.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" + ) + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO5 求解 + print("Running ENO5 solver...") + config_eno5 = CfdConfig() + config_eno5.with_reconstruction("eno", 5) + config_eno5.dt = 0.0025 + config_eno5.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno5 = LinearAdvectionProblem(config_eno5) + cfd_eno5 = Cfd(problem_eno5, mesh) # ← 注入 problem + cfd_eno5.run() + + # 3. 配置并运行 WENO5求解 + print("Running WENO5 solver...") + config_weno5 = CfdConfig() + config_weno5.with_reconstruction("weno", 5) + config_weno5.dt = 0.0025 + config_weno5.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno5 = LinearAdvectionProblem(config_weno5) + cfd_weno5 = Cfd(problem_weno5, mesh) # ← 注入 problem + cfd_weno5.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno5.result, + weno_result=cfd_weno5.result, + save_path="eno_weno_comparison.png" + ) + + +if __name__ == "__main__": + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/boundary.py b/example/1d-linear-convection/weno3/python-v1/02b/src/boundary.py new file mode 100644 index 00000000..2d2cfbaf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/boundary.py @@ -0,0 +1,104 @@ +from abc import ABC, abstractmethod +from core.registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from core.registry import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/config.py b/example/1d-linear-convection/weno3/python-v1/02b/src/config.py new file mode 100644 index 00000000..6432c80e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/config.py @@ -0,0 +1,45 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + #self.ic_type = "sin" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # "rusanov", "engquist-osher" + #self.flux_type = "engquist-osher" # "rusanov", "engquist-osher" + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + print(f"scheme={scheme}") + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/core/registry.py b/example/1d-linear-convection/weno3/python-v1/02b/src/core/registry.py new file mode 100644 index 00000000..4a52afc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/core/registry.py @@ -0,0 +1,83 @@ +# core/registry.py +""" +统一注册系统 + 通用工厂 +- ComponentRegistry: 组件注册表 +- register_component: 装饰器 +- BaseFactory: 通用工厂类 +""" + +from typing import Dict, Type, Any + + +# ==================== 1. 注册表核心 ==================== +class ComponentRegistry: + """组件注册表""" + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True + + @classmethod + def set_verbose(cls, verbose: bool): + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + #print(f"ComponentRegistry register cls={cls}") + #print(f"ComponentRegistry register name={name}") + #print(f"ComponentRegistry register component_class={component_class}") + if category not in cls._registries: + cls._registries[category] = {} + if name in cls._registries[category]: + if cls._registries[category][name] != component_class and cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + if category not in cls._registries: + raise ValueError(f"❌ 未知类别: {category} (可用: {list(cls._registries.keys())})") + if name not in cls._registries[category]: + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {list(cls._registries[category].keys())})") + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) for cat, comps in cls._registries.items()} + + +# ==================== 2. 装饰器 ==================== +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + #print(f"register_component decorator name={name}") + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + +# ==================== 3. 通用工厂 ==================== +class BaseFactory: + """通用工厂基类""" + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + name_lower = name.lower() + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + available = ComponentRegistry.list_all().get(category, []) + if available: + error_msg = f"不支持的 {category} 类型 '{name}'。可用类型:{available}" + else: + error_msg = f"不支持的 {category} 类型 '{name}'(无已注册组件)" + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/domain.py b/example/1d-linear-convection/weno3/python-v1/02b/src/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/equations/base.py b/example/1d-linear-convection/weno3/python-v1/02b/src/equations/base.py new file mode 100644 index 00000000..92e54f12 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/equations/base.py @@ -0,0 +1,56 @@ +# equations/base.py + +from abc import ABC, abstractmethod +import numpy as np + +class Equation(ABC): + """ + 控制方程抽象基类 + 所有物理方程(线性对流、Euler、MHD)必须继承此类 + """ + + @property + @abstractmethod + def num_equations(self) -> int: + """返回方程组变量数(标量=1,Euler=3,MHD=8)""" + pass + + @abstractmethod + def flux(self, u): + """ + 计算通量函数 f(u) + :param u: 状态向量 (num_equations,) + :return: 通量向量 (num_equations,) + """ + pass + + @abstractmethod + def max_wave_speed(self, u): + """ + 计算最大波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + @abstractmethod + def wave_speed(self): + """ + 计算波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + def exact_solution(self, x, t, initial_condition_func): + """ + 可选:提供解析解 + 子类可重写;若无解析解,调用方应捕获 NotImplementedError + :param x: 空间坐标数组 (ncells,) + :param t: 时间 + :param initial_condition_func: 初值函数 u0(x) -> (num_equations, ncells) + :return: 解 u(x,t) -> (num_equations, ncells) + """ + raise NotImplementedError( + f"Equation '{self.__class__.__name__}' does not provide an exact solution." + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/equations/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/02b/src/equations/linear_advection.py new file mode 100644 index 00000000..26aa026b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/equations/linear_advection.py @@ -0,0 +1,42 @@ +# equations/linear_advection.py + +import numpy as np +from .base import Equation + +class LinearAdvectionEquation(Equation): + """ + 线性对流方程: u_t + c * u_x = 0 + 支持标量(num_equations=1) + """ + + def __init__(self, wave_speed: float): + self.c = wave_speed + + @property + def num_equations(self) -> int: + return 1 + + def flux(self, u): + """f(u) = c * u""" + return self.c * u + + def max_wave_speed(self, u): + """最大波速 = |c|""" + return abs(self.c) + + def wave_speed(self): + return self.c + + + def exact_solution(self, x, t, initial_condition_func): + """ + 解析解: u(x, t) = u0(x - c * t) + 支持周期边界 + """ + if len(x) == 0: + return np.zeros((1, 0)) + + L = x[-1] - x[0] # 假设均匀周期网格 + x_shifted = (x - self.c * t + L) % L + u0 = initial_condition_func(x_shifted) # (1, ncells) + return u0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/flux/__init__.py b/example/1d-linear-convection/weno3/python-v1/02b/src/flux/__init__.py new file mode 100644 index 00000000..432a36cb --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/flux/__init__.py @@ -0,0 +1,7 @@ +# flux/__init__.py +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 确保子模块被导入以触发注册 +from . import rusanov +from . import engquist_osher \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/flux/base.py b/example/1d-linear-convection/weno3/python-v1/02b/src/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python-v1/02b/src/flux/engquist_osher.py new file mode 100644 index 00000000..eb7b8cdf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/flux/engquist_osher.py @@ -0,0 +1,20 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + eq = self.cfd.problem.equation + for i in range(self.mesh.nnodes): + c = eq.wave_speed() + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/flux/factory.py b/example/1d-linear-convection/weno3/python-v1/02b/src/flux/factory.py new file mode 100644 index 00000000..5ae77c3f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/flux/factory.py @@ -0,0 +1,25 @@ +# flux/factory.py +""" +通量计算器专用工厂(封装注册细节,提供清晰接口) +符合你希望“将创建逻辑封装在工厂中”的设计原则 +""" + +from core.registry import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/flux/rusanov.py b/example/1d-linear-convection/weno3/python-v1/02b/src/flux/rusanov.py new file mode 100644 index 00000000..baa7c62c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/flux/rusanov.py @@ -0,0 +1,25 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量,使用 Equation 解耦物理参数""" + + def compute(self, q_face_left, q_face_right, flux): + # 从 cfd 获取 equation(与 Julia 对齐) + eq = self.cfd.problem.equation + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = eq.wave_speed() + c_R = eq.wave_speed() + # 通过 equation 计算通量和波速 + F_L = eq.flux(u_L) + F_R = eq.flux(u_R) + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/initial_condition/__init__.py b/example/1d-linear-convection/weno3/python-v1/02b/src/initial_condition/__init__.py new file mode 100644 index 00000000..9eab71d5 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/initial_condition/__init__.py @@ -0,0 +1,12 @@ +# src/initial_conditions/__init__.py +""" +初始条件模块 +提供各种初始条件实现和工厂创建接口 +""" + +from .base import InitialCondition +from .factory import InitialConditionFactory + +from .step import StepFunctionIC +from .sine import SineWaveIC +from .gaussian import GaussianPulseIC diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/initial_condition/base.py b/example/1d-linear-convection/weno3/python-v1/02b/src/initial_condition/base.py new file mode 100644 index 00000000..8a33a0d5 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/initial_condition/base.py @@ -0,0 +1,25 @@ +# src/initial_conditions/base.py +from abc import ABC, abstractmethod +import numpy as np + +class InitialCondition(ABC): + """初始条件抽象基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + """内部辅助方法:将值应用到物理区域""" + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/initial_condition/factory.py b/example/1d-linear-convection/weno3/python-v1/02b/src/initial_condition/factory.py new file mode 100644 index 00000000..2a7558b2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/initial_condition/factory.py @@ -0,0 +1,24 @@ +# src/initial_conditions/factory.py +from core.registry import BaseFactory +from .base import InitialCondition + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + config: 配置对象,包含ic_type属性 + + Returns: + 初始条件实例 + """ + return BaseFactory.create_component('initial_condition', config.ic_type, config) + + @classmethod + def get_available_types(cls): + """获取所有可用的初始条件类型""" + return BaseFactory.get_available_components('initial_condition') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/initial_condition/gaussian.py b/example/1d-linear-convection/weno3/python-v1/02b/src/initial_condition/gaussian.py new file mode 100644 index 00000000..1dad463b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/initial_condition/gaussian.py @@ -0,0 +1,16 @@ +# src/initial_conditions/gaussian.py +import numpy as np +from .base import InitialCondition +from core.registry import register_component + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/initial_condition/sine.py b/example/1d-linear-convection/weno3/python-v1/02b/src/initial_condition/sine.py new file mode 100644 index 00000000..c061fa5a --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/initial_condition/sine.py @@ -0,0 +1,15 @@ +# src/initial_conditions/sine.py +import numpy as np +from .base import InitialCondition +from core.registry import register_component + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/initial_condition/step.py b/example/1d-linear-convection/weno3/python-v1/02b/src/initial_condition/step.py new file mode 100644 index 00000000..ff1120b2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/initial_condition/step.py @@ -0,0 +1,17 @@ +# src/initial_conditions/step.py +import numpy as np +from .base import InitialCondition +from core.registry import register_component + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/mesh.py b/example/1d-linear-convection/weno3/python-v1/02b/src/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/plotter.py b/example/1d-linear-convection/weno3/python-v1/02b/src/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/problem.py b/example/1d-linear-convection/weno3/python-v1/02b/src/problem.py new file mode 100644 index 00000000..53719f26 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/problem.py @@ -0,0 +1,38 @@ +# problem.py +""" +问题定义模块:每个 Problem 子类代表一个完整测试用例 +包含初始条件、解析解(可选)、物理方程等 +""" + +from abc import ABC, abstractmethod +from initial_condition import InitialConditionFactory + + +class Problem(ABC): + """ + 抽象问题基类 + 每个具体问题(如线性对流、Sod 激波管)应继承此类 + """ + def __init__(self, config): + self.config = config + + @abstractmethod + def initial_condition(self): + """ + 返回 InitialCondition 实例 + """ + pass + + def exact_solution(self, cfd): + """ + 可选:返回解析解(数值数组) + 若无解析解,可抛出 NotImplementedError 或返回 None + """ + x = cfd.domain.mesh.xcc + raise NotImplementedError( + f"Problem '{self.__class__.__name__}' does not provide an exact solution." + ) + + @property + def name(self): + return self.__class__.__name__ \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/problems/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/02b/src/problems/linear_advection.py new file mode 100644 index 00000000..b6d548f5 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/problems/linear_advection.py @@ -0,0 +1,39 @@ +# problems/linear_advection.py + +import numpy as np +from problem import Problem +from equations.linear_advection import LinearAdvectionEquation +from initial_condition import InitialConditionFactory + +class LinearAdvectionProblem(Problem): + """ + 线性对流问题:u_t + c u_x = 0 + 使用周期边界 + 任意初始条件 + """ + + def __init__(self, config): + super().__init__(config) + # 持有物理方程实例 + self.equation = LinearAdvectionEquation(wave_speed=config.wave_speed) + self._ic_cache = None # 缓存 IC 实例 + + def initial_condition(self): + """返回初始条件实例""" + if self._ic_cache is None: + self._ic_cache = InitialConditionFactory.create(self.config) + return self._ic_cache + + def exact_solution(self, cfd): + """ + 委托给 Equation 计算解析解 + """ + ic = self.initial_condition() + # 包装 evaluate_at 以返回 (1, ncells) + def vectorized_ic(x): + u0_scalar = ic.evaluate_at(x) # (ncells,) + return u0_scalar[np.newaxis, :] # (1, ncells) + + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + u_exact = self.equation.exact_solution(x, t, vectorized_ic) + return u_exact[0] # 返回标量数组 (ncells,) 以兼容现有绘图逻辑 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python-v1/02b/src/reconstructor/__init__.py new file mode 100644 index 00000000..d705bb98 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/reconstructor/__init__.py @@ -0,0 +1,6 @@ +# reconstructor/__init__.py +from .base import Reconstructor +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from .weno5 import Weno5Reconstructor +from .factory import ReconstructorFactory diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/reconstructor/base.py b/example/1d-linear-convection/weno3/python-v1/02b/src/reconstructor/base.py new file mode 100644 index 00000000..de00327c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def compute_face_values(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/reconstructor/eno.py b/example/1d-linear-convection/weno3/python-v1/02b/src/reconstructor/eno.py new file mode 100644 index 00000000..60699adf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/reconstructor/eno.py @@ -0,0 +1,96 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.initialize(self.config.spatial_order, self.domain.ntcells) + + def initialize(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def compute_face_values(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/reconstructor/factory.py b/example/1d-linear-convection/weno3/python-v1/02b/src/reconstructor/factory.py new file mode 100644 index 00000000..e287ab84 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/reconstructor/factory.py @@ -0,0 +1,14 @@ +# reconstructor/factory.py +from core.registry import BaseFactory + +class ReconstructorFactory: + @staticmethod + def create(cfd): + config = cfd.config + scheme = config.recon_scheme.lower() + + if scheme.startswith("weno"): + if scheme == "weno": + order = getattr(config, 'spatial_order', None) + scheme = f"weno{order}" + return BaseFactory.create_component('reconstructor', scheme, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python-v1/02b/src/reconstructor/weno3.py new file mode 100644 index 00000000..f45eb35f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/reconstructor/weno3.py @@ -0,0 +1,62 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def __init__(self, cfd): + self.cfd = cfd + + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/reconstructor/weno5.py b/example/1d-linear-convection/weno3/python-v1/02b/src/reconstructor/weno5.py new file mode 100644 index 00000000..9d665275 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/reconstructor/weno5.py @@ -0,0 +1,71 @@ +# reconstructor/weno5.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno5') +class Weno5Reconstructor(Reconstructor): + def __init__(self, cfd): + self.cfd = cfd + + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3, v4, v5) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3, v4, v5) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 1.0/10.0 + d1 = 3.0/5.0 + d2 = 3.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = 1.0/3.0*v1-7.0/6.0*v2+11.0/6.0*v3 # r=2 + q1 = -1.0/6.0*v2+5.0/6.0*v3+1.0/3.0*v4 # r=1 + q2 = 1.0/3.0*v3+5.0/6.0*v4-1.0/6.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 3.0/10.0 + d1 = 3.0/5.0 + d2 = 1.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = -1.0/6.0*v1+5.0/6.0*v2+1.0/3.0*v3 # r=2 + q1 = 1.0/3.0*v2+5.0/6.0*v3-1.0/6.0*v4 # r=1 + q2 = 11.0/6.0*v3-7.0/6.0*v4+1.0/3.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/residual.py b/example/1d-linear-convection/weno3/python-v1/02b/src/residual.py new file mode 100644 index 00000000..27d5a0c4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/residual.py @@ -0,0 +1,41 @@ +# residual.py + +from flux.factory import FluxCalculatorFactory +from reconstructor import ReconstructorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + + #self.reconstructor = ReconstructorFactory.create(self.config, self.domain) + self.reconstructor = ReconstructorFactory.create(cfd) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._compute_face_values() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _compute_face_values(self): + """私有方法:界面值重建""" + self.reconstructor.compute_face_values(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/result.py b/example/1d-linear-convection/weno3/python-v1/02b/src/result.py new file mode 100644 index 00000000..5e572216 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/result.py @@ -0,0 +1,25 @@ +# result.py + +class ResultAssembler: + @staticmethod + def assemble(cfd: 'Cfd') -> dict: + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied].copy() + + # ✅ 从 problem 获取解析解(唯一正确路径) + try: + analytical = cfd.problem.exact_solution(cfd) + except NotImplementedError: + analytical = None # 或 np.full_like(u_numerical, np.nan) + + return { + "x": cfd.domain.mesh.xcc, + "numerical": u_numerical, + "analytical": analytical, + "config": { + "scheme": cfd.config.recon_scheme, + "order": cfd.config.spatial_order, + "rk_order": cfd.config.rk_order, + "final_time": cfd.config.final_time, + "problem": cfd.problem.name, + } + } \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/solution.py b/example/1d-linear-convection/weno3/python-v1/02b/src/solution.py new file mode 100644 index 00000000..90666c34 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/solution.py @@ -0,0 +1,32 @@ +# solution.py +import numpy as np + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/solver.py b/example/1d-linear-convection/weno3/python-v1/02b/src/solver.py new file mode 100644 index 00000000..316be231 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/solver.py @@ -0,0 +1,67 @@ +# solver.py + +from mesh import Mesh +from domain import Domain +from solution import Solution +from config import CfdConfig +from result import ResultAssembler +from problem import Problem +from initial_condition import InitialConditionFactory +from boundary import BoundaryConditionFactory +from residual import ResidualCalculator +from time_integration import TimeIntegrator,TimeIntegratorFactory + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, problem: Problem, mesh): + self.problem = problem + self.config = problem.config + self.domain = Domain(problem.config, mesh) + self.solution = Solution(problem.config, self.domain) + + self.initial_condition = InitialConditionFactory.create(self.config) + self.boundary_condition = BoundaryConditionFactory.create(self) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + + # ==================== 公共接口(供 TimeIntegrator 调用) ==================== + def compute_residual(self): + """计算物理残差(封装重建→通量→散度)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件""" + self.boundary_condition.apply(self.solution.u) + + # ==================== 初始化 ==================== + def initialize(self): + """初始化全场:先 IC,再 BC,最后同步 old field""" + self.initial_condition.apply(self.solution) + # 应用边界条件到初始场 + self.apply_boundary() + # 同步 old field + self.solution.update_old_field() + + + def run(self): + self.initialize() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + self.result = ResultAssembler.assemble(self) + return self.result["numerical"] + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/time_integration/__init__.py b/example/1d-linear-convection/weno3/python-v1/02b/src/time_integration/__init__.py new file mode 100644 index 00000000..6342de3b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/time_integration/__init__.py @@ -0,0 +1,8 @@ +# time_integration/__init__.py + +# 导出统一接口 +from .factory import TimeIntegratorFactory +from .base import TimeIntegrator + +# 触发子模块注册(关键!) +from . import rk1, rk2, rk3 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/time_integration/base.py b/example/1d-linear-convection/weno3/python-v1/02b/src/time_integration/base.py new file mode 100644 index 00000000..0cdf29a4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/time_integration/base.py @@ -0,0 +1,27 @@ +# time_integration/base.py + +from abc import ABC, abstractmethod + +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + + @abstractmethod + def step(self, dt): + pass + + def compute_residual(self): + """计算残差(委托给 Cfd)""" + self.cfd.compute_residual() + + def apply_boundary(self): + """应用边界条件(委托给 Cfd)""" + self.cfd.apply_boundary() + + def map_idx(self, i): + """物理网格索引 → 残差数组索引""" + return i - self.domain.ist \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/time_integration/factory.py b/example/1d-linear-convection/weno3/python-v1/02b/src/time_integration/factory.py new file mode 100644 index 00000000..94662db2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/time_integration/factory.py @@ -0,0 +1,10 @@ +# time_integration/factory.py + +from core.registry import BaseFactory + +class TimeIntegratorFactory: + @staticmethod + def create(cfd) -> 'TimeIntegrator': + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/time_integration/rk1.py b/example/1d-linear-convection/weno3/python-v1/02b/src/time_integration/rk1.py new file mode 100644 index 00000000..b4c8a021 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/time_integration/rk1.py @@ -0,0 +1,15 @@ +# time_integration/rk1.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/time_integration/rk2.py b/example/1d-linear-convection/weno3/python-v1/02b/src/time_integration/rk2.py new file mode 100644 index 00000000..6d2be304 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/time_integration/rk2.py @@ -0,0 +1,29 @@ +# time_integration/rk2.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + 0.5 * self.solution.un[i] + + 0.5 * self.solution.u[i] + + 0.5 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02b/src/time_integration/rk3.py b/example/1d-linear-convection/weno3/python-v1/02b/src/time_integration/rk3.py new file mode 100644 index 00000000..c70791e1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02b/src/time_integration/rk3.py @@ -0,0 +1,43 @@ +# time_integration/rk3.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # Stage 1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # Stage 2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = ( + 0.75 * self.solution.un[i] + + 0.25 * self.solution.u[i] + + 0.25 * dt * self.solution.res[j] + ) + self.solution.u[:] = u2 + self.apply_boundary() + + # Stage 3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + c1 * self.solution.un[i] + + c2 * self.solution.u[i] + + c3 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/example/run_eno_weno.py b/example/1d-linear-convection/weno3/python-v1/02c/example/run_eno_weno.py new file mode 100644 index 00000000..c4063cb7 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/example/run_eno_weno.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +# example/run_eno_weno.py + +import sys +import os +src_path = os.path.join(os.path.dirname(__file__), '..', 'src') +if src_path not in sys.path: + sys.path.insert(0, src_path) + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +from problems.linear_advection import LinearAdvectionProblem + + +def performEnoWenoAnalysisBAK(): + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + print("Running ENO3 solver...") + config_eno3 = CfdConfig() + config_eno3.with_reconstruction("eno", 3) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno3 = LinearAdvectionProblem(config_eno3) + cfd_eno3 = Cfd(problem_eno3, mesh) # ← 注入 problem + cfd_eno3.run() + + # 3. 配置并运行 WENO3 求解 + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno3 = LinearAdvectionProblem(config_weno3) + cfd_weno3 = Cfd(problem_weno3, mesh) # ← 注入 problem + cfd_weno3.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" + ) + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO5 求解 + print("Running ENO5 solver...") + config_eno5 = CfdConfig() + config_eno5.with_reconstruction("eno", 5) + config_eno5.dt = 0.0025 + config_eno5.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno5 = LinearAdvectionProblem(config_eno5) + cfd_eno5 = Cfd(problem_eno5, mesh) # ← 注入 problem + cfd_eno5.run() + + # 3. 配置并运行 WENO5求解 + print("Running WENO5 solver...") + config_weno5 = CfdConfig() + config_weno5.with_reconstruction("weno", 5) + config_weno5.dt = 0.0025 + config_weno5.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno5 = LinearAdvectionProblem(config_weno5) + cfd_weno5 = Cfd(problem_weno5, mesh) # ← 注入 problem + cfd_weno5.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno5.result, + weno_result=cfd_weno5.result, + save_path="eno_weno_comparison.png" + ) + + +if __name__ == "__main__": + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/boundary/__init__.py b/example/1d-linear-convection/weno3/python-v1/02c/src/boundary/__init__.py new file mode 100644 index 00000000..20120e8a --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/boundary/__init__.py @@ -0,0 +1,21 @@ +# src/boundary/__init__.py +""" +边界条件模块 +提供各种边界条件实现和工厂创建接口 +""" + +from .base import BoundaryCondition +from .factory import BoundaryConditionFactory + +# 导入具体类以触发注册 +from .periodic import PeriodicBoundary +from .dirichlet import DirichletBoundary +from .neumann import NeumannBoundary + +__all__ = [ + 'BoundaryCondition', + 'BoundaryConditionFactory', + 'PeriodicBoundary', + 'DirichletBoundary', + 'NeumannBoundary', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/boundary/base.py b/example/1d-linear-convection/weno3/python-v1/02c/src/boundary/base.py new file mode 100644 index 00000000..cddf649d --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/boundary/base.py @@ -0,0 +1,18 @@ +# src/boundary/base.py +from abc import ABC, abstractmethod + +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/boundary/dirichlet.py b/example/1d-linear-convection/weno3/python-v1/02c/src/boundary/dirichlet.py new file mode 100644 index 00000000..1eff3699 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/boundary/dirichlet.py @@ -0,0 +1,27 @@ +# src/boundary/dirichlet.py +from .base import BoundaryCondition +from core.registry import register_component + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/boundary/factory.py b/example/1d-linear-convection/weno3/python-v1/02c/src/boundary/factory.py new file mode 100644 index 00000000..4bd7ae55 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/boundary/factory.py @@ -0,0 +1,24 @@ +# src/boundary/factory.py +from core.registry import BaseFactory + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + + @staticmethod + def get_available_types(): + """获取所有可用的边界条件类型""" + return BaseFactory.get_available_components('boundary') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/boundary/neumann.py b/example/1d-linear-convection/weno3/python-v1/02c/src/boundary/neumann.py new file mode 100644 index 00000000..6eb72f64 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/boundary/neumann.py @@ -0,0 +1,23 @@ +# src/boundary/neumann.py +from .base import BoundaryCondition +from core.registry import register_component + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/boundary/periodic.py b/example/1d-linear-convection/weno3/python-v1/02c/src/boundary/periodic.py new file mode 100644 index 00000000..bd601d95 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/boundary/periodic.py @@ -0,0 +1,19 @@ +# src/boundary/periodic.py +from .base import BoundaryCondition +from core.registry import register_component + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/config.py b/example/1d-linear-convection/weno3/python-v1/02c/src/config.py new file mode 100644 index 00000000..6432c80e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/config.py @@ -0,0 +1,45 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + #self.ic_type = "sin" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # "rusanov", "engquist-osher" + #self.flux_type = "engquist-osher" # "rusanov", "engquist-osher" + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + print(f"scheme={scheme}") + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/core/registry.py b/example/1d-linear-convection/weno3/python-v1/02c/src/core/registry.py new file mode 100644 index 00000000..4a52afc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/core/registry.py @@ -0,0 +1,83 @@ +# core/registry.py +""" +统一注册系统 + 通用工厂 +- ComponentRegistry: 组件注册表 +- register_component: 装饰器 +- BaseFactory: 通用工厂类 +""" + +from typing import Dict, Type, Any + + +# ==================== 1. 注册表核心 ==================== +class ComponentRegistry: + """组件注册表""" + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True + + @classmethod + def set_verbose(cls, verbose: bool): + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + #print(f"ComponentRegistry register cls={cls}") + #print(f"ComponentRegistry register name={name}") + #print(f"ComponentRegistry register component_class={component_class}") + if category not in cls._registries: + cls._registries[category] = {} + if name in cls._registries[category]: + if cls._registries[category][name] != component_class and cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + if category not in cls._registries: + raise ValueError(f"❌ 未知类别: {category} (可用: {list(cls._registries.keys())})") + if name not in cls._registries[category]: + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {list(cls._registries[category].keys())})") + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) for cat, comps in cls._registries.items()} + + +# ==================== 2. 装饰器 ==================== +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + #print(f"register_component decorator name={name}") + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + +# ==================== 3. 通用工厂 ==================== +class BaseFactory: + """通用工厂基类""" + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + name_lower = name.lower() + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + available = ComponentRegistry.list_all().get(category, []) + if available: + error_msg = f"不支持的 {category} 类型 '{name}'。可用类型:{available}" + else: + error_msg = f"不支持的 {category} 类型 '{name}'(无已注册组件)" + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/domain.py b/example/1d-linear-convection/weno3/python-v1/02c/src/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/equations/base.py b/example/1d-linear-convection/weno3/python-v1/02c/src/equations/base.py new file mode 100644 index 00000000..92e54f12 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/equations/base.py @@ -0,0 +1,56 @@ +# equations/base.py + +from abc import ABC, abstractmethod +import numpy as np + +class Equation(ABC): + """ + 控制方程抽象基类 + 所有物理方程(线性对流、Euler、MHD)必须继承此类 + """ + + @property + @abstractmethod + def num_equations(self) -> int: + """返回方程组变量数(标量=1,Euler=3,MHD=8)""" + pass + + @abstractmethod + def flux(self, u): + """ + 计算通量函数 f(u) + :param u: 状态向量 (num_equations,) + :return: 通量向量 (num_equations,) + """ + pass + + @abstractmethod + def max_wave_speed(self, u): + """ + 计算最大波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + @abstractmethod + def wave_speed(self): + """ + 计算波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + def exact_solution(self, x, t, initial_condition_func): + """ + 可选:提供解析解 + 子类可重写;若无解析解,调用方应捕获 NotImplementedError + :param x: 空间坐标数组 (ncells,) + :param t: 时间 + :param initial_condition_func: 初值函数 u0(x) -> (num_equations, ncells) + :return: 解 u(x,t) -> (num_equations, ncells) + """ + raise NotImplementedError( + f"Equation '{self.__class__.__name__}' does not provide an exact solution." + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/equations/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/02c/src/equations/linear_advection.py new file mode 100644 index 00000000..26aa026b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/equations/linear_advection.py @@ -0,0 +1,42 @@ +# equations/linear_advection.py + +import numpy as np +from .base import Equation + +class LinearAdvectionEquation(Equation): + """ + 线性对流方程: u_t + c * u_x = 0 + 支持标量(num_equations=1) + """ + + def __init__(self, wave_speed: float): + self.c = wave_speed + + @property + def num_equations(self) -> int: + return 1 + + def flux(self, u): + """f(u) = c * u""" + return self.c * u + + def max_wave_speed(self, u): + """最大波速 = |c|""" + return abs(self.c) + + def wave_speed(self): + return self.c + + + def exact_solution(self, x, t, initial_condition_func): + """ + 解析解: u(x, t) = u0(x - c * t) + 支持周期边界 + """ + if len(x) == 0: + return np.zeros((1, 0)) + + L = x[-1] - x[0] # 假设均匀周期网格 + x_shifted = (x - self.c * t + L) % L + u0 = initial_condition_func(x_shifted) # (1, ncells) + return u0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/flux/__init__.py b/example/1d-linear-convection/weno3/python-v1/02c/src/flux/__init__.py new file mode 100644 index 00000000..432a36cb --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/flux/__init__.py @@ -0,0 +1,7 @@ +# flux/__init__.py +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 确保子模块被导入以触发注册 +from . import rusanov +from . import engquist_osher \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/flux/base.py b/example/1d-linear-convection/weno3/python-v1/02c/src/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python-v1/02c/src/flux/engquist_osher.py new file mode 100644 index 00000000..eb7b8cdf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/flux/engquist_osher.py @@ -0,0 +1,20 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + eq = self.cfd.problem.equation + for i in range(self.mesh.nnodes): + c = eq.wave_speed() + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/flux/factory.py b/example/1d-linear-convection/weno3/python-v1/02c/src/flux/factory.py new file mode 100644 index 00000000..5ae77c3f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/flux/factory.py @@ -0,0 +1,25 @@ +# flux/factory.py +""" +通量计算器专用工厂(封装注册细节,提供清晰接口) +符合你希望“将创建逻辑封装在工厂中”的设计原则 +""" + +from core.registry import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/flux/rusanov.py b/example/1d-linear-convection/weno3/python-v1/02c/src/flux/rusanov.py new file mode 100644 index 00000000..baa7c62c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/flux/rusanov.py @@ -0,0 +1,25 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量,使用 Equation 解耦物理参数""" + + def compute(self, q_face_left, q_face_right, flux): + # 从 cfd 获取 equation(与 Julia 对齐) + eq = self.cfd.problem.equation + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = eq.wave_speed() + c_R = eq.wave_speed() + # 通过 equation 计算通量和波速 + F_L = eq.flux(u_L) + F_R = eq.flux(u_R) + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/initial_condition/__init__.py b/example/1d-linear-convection/weno3/python-v1/02c/src/initial_condition/__init__.py new file mode 100644 index 00000000..9eab71d5 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/initial_condition/__init__.py @@ -0,0 +1,12 @@ +# src/initial_conditions/__init__.py +""" +初始条件模块 +提供各种初始条件实现和工厂创建接口 +""" + +from .base import InitialCondition +from .factory import InitialConditionFactory + +from .step import StepFunctionIC +from .sine import SineWaveIC +from .gaussian import GaussianPulseIC diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/initial_condition/base.py b/example/1d-linear-convection/weno3/python-v1/02c/src/initial_condition/base.py new file mode 100644 index 00000000..8a33a0d5 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/initial_condition/base.py @@ -0,0 +1,25 @@ +# src/initial_conditions/base.py +from abc import ABC, abstractmethod +import numpy as np + +class InitialCondition(ABC): + """初始条件抽象基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + """内部辅助方法:将值应用到物理区域""" + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/initial_condition/factory.py b/example/1d-linear-convection/weno3/python-v1/02c/src/initial_condition/factory.py new file mode 100644 index 00000000..2a7558b2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/initial_condition/factory.py @@ -0,0 +1,24 @@ +# src/initial_conditions/factory.py +from core.registry import BaseFactory +from .base import InitialCondition + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + config: 配置对象,包含ic_type属性 + + Returns: + 初始条件实例 + """ + return BaseFactory.create_component('initial_condition', config.ic_type, config) + + @classmethod + def get_available_types(cls): + """获取所有可用的初始条件类型""" + return BaseFactory.get_available_components('initial_condition') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/initial_condition/gaussian.py b/example/1d-linear-convection/weno3/python-v1/02c/src/initial_condition/gaussian.py new file mode 100644 index 00000000..1dad463b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/initial_condition/gaussian.py @@ -0,0 +1,16 @@ +# src/initial_conditions/gaussian.py +import numpy as np +from .base import InitialCondition +from core.registry import register_component + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/initial_condition/sine.py b/example/1d-linear-convection/weno3/python-v1/02c/src/initial_condition/sine.py new file mode 100644 index 00000000..c061fa5a --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/initial_condition/sine.py @@ -0,0 +1,15 @@ +# src/initial_conditions/sine.py +import numpy as np +from .base import InitialCondition +from core.registry import register_component + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/initial_condition/step.py b/example/1d-linear-convection/weno3/python-v1/02c/src/initial_condition/step.py new file mode 100644 index 00000000..ff1120b2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/initial_condition/step.py @@ -0,0 +1,17 @@ +# src/initial_conditions/step.py +import numpy as np +from .base import InitialCondition +from core.registry import register_component + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/mesh.py b/example/1d-linear-convection/weno3/python-v1/02c/src/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/plotter.py b/example/1d-linear-convection/weno3/python-v1/02c/src/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/problem.py b/example/1d-linear-convection/weno3/python-v1/02c/src/problem.py new file mode 100644 index 00000000..53719f26 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/problem.py @@ -0,0 +1,38 @@ +# problem.py +""" +问题定义模块:每个 Problem 子类代表一个完整测试用例 +包含初始条件、解析解(可选)、物理方程等 +""" + +from abc import ABC, abstractmethod +from initial_condition import InitialConditionFactory + + +class Problem(ABC): + """ + 抽象问题基类 + 每个具体问题(如线性对流、Sod 激波管)应继承此类 + """ + def __init__(self, config): + self.config = config + + @abstractmethod + def initial_condition(self): + """ + 返回 InitialCondition 实例 + """ + pass + + def exact_solution(self, cfd): + """ + 可选:返回解析解(数值数组) + 若无解析解,可抛出 NotImplementedError 或返回 None + """ + x = cfd.domain.mesh.xcc + raise NotImplementedError( + f"Problem '{self.__class__.__name__}' does not provide an exact solution." + ) + + @property + def name(self): + return self.__class__.__name__ \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/problems/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/02c/src/problems/linear_advection.py new file mode 100644 index 00000000..b6d548f5 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/problems/linear_advection.py @@ -0,0 +1,39 @@ +# problems/linear_advection.py + +import numpy as np +from problem import Problem +from equations.linear_advection import LinearAdvectionEquation +from initial_condition import InitialConditionFactory + +class LinearAdvectionProblem(Problem): + """ + 线性对流问题:u_t + c u_x = 0 + 使用周期边界 + 任意初始条件 + """ + + def __init__(self, config): + super().__init__(config) + # 持有物理方程实例 + self.equation = LinearAdvectionEquation(wave_speed=config.wave_speed) + self._ic_cache = None # 缓存 IC 实例 + + def initial_condition(self): + """返回初始条件实例""" + if self._ic_cache is None: + self._ic_cache = InitialConditionFactory.create(self.config) + return self._ic_cache + + def exact_solution(self, cfd): + """ + 委托给 Equation 计算解析解 + """ + ic = self.initial_condition() + # 包装 evaluate_at 以返回 (1, ncells) + def vectorized_ic(x): + u0_scalar = ic.evaluate_at(x) # (ncells,) + return u0_scalar[np.newaxis, :] # (1, ncells) + + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + u_exact = self.equation.exact_solution(x, t, vectorized_ic) + return u_exact[0] # 返回标量数组 (ncells,) 以兼容现有绘图逻辑 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python-v1/02c/src/reconstructor/__init__.py new file mode 100644 index 00000000..d705bb98 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/reconstructor/__init__.py @@ -0,0 +1,6 @@ +# reconstructor/__init__.py +from .base import Reconstructor +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from .weno5 import Weno5Reconstructor +from .factory import ReconstructorFactory diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/reconstructor/base.py b/example/1d-linear-convection/weno3/python-v1/02c/src/reconstructor/base.py new file mode 100644 index 00000000..de00327c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def compute_face_values(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/reconstructor/eno.py b/example/1d-linear-convection/weno3/python-v1/02c/src/reconstructor/eno.py new file mode 100644 index 00000000..60699adf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/reconstructor/eno.py @@ -0,0 +1,96 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.initialize(self.config.spatial_order, self.domain.ntcells) + + def initialize(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def compute_face_values(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/reconstructor/factory.py b/example/1d-linear-convection/weno3/python-v1/02c/src/reconstructor/factory.py new file mode 100644 index 00000000..e287ab84 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/reconstructor/factory.py @@ -0,0 +1,14 @@ +# reconstructor/factory.py +from core.registry import BaseFactory + +class ReconstructorFactory: + @staticmethod + def create(cfd): + config = cfd.config + scheme = config.recon_scheme.lower() + + if scheme.startswith("weno"): + if scheme == "weno": + order = getattr(config, 'spatial_order', None) + scheme = f"weno{order}" + return BaseFactory.create_component('reconstructor', scheme, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python-v1/02c/src/reconstructor/weno3.py new file mode 100644 index 00000000..f45eb35f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/reconstructor/weno3.py @@ -0,0 +1,62 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def __init__(self, cfd): + self.cfd = cfd + + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/reconstructor/weno5.py b/example/1d-linear-convection/weno3/python-v1/02c/src/reconstructor/weno5.py new file mode 100644 index 00000000..9d665275 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/reconstructor/weno5.py @@ -0,0 +1,71 @@ +# reconstructor/weno5.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno5') +class Weno5Reconstructor(Reconstructor): + def __init__(self, cfd): + self.cfd = cfd + + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3, v4, v5) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3, v4, v5) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 1.0/10.0 + d1 = 3.0/5.0 + d2 = 3.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = 1.0/3.0*v1-7.0/6.0*v2+11.0/6.0*v3 # r=2 + q1 = -1.0/6.0*v2+5.0/6.0*v3+1.0/3.0*v4 # r=1 + q2 = 1.0/3.0*v3+5.0/6.0*v4-1.0/6.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 3.0/10.0 + d1 = 3.0/5.0 + d2 = 1.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = -1.0/6.0*v1+5.0/6.0*v2+1.0/3.0*v3 # r=2 + q1 = 1.0/3.0*v2+5.0/6.0*v3-1.0/6.0*v4 # r=1 + q2 = 11.0/6.0*v3-7.0/6.0*v4+1.0/3.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/residual.py b/example/1d-linear-convection/weno3/python-v1/02c/src/residual.py new file mode 100644 index 00000000..27d5a0c4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/residual.py @@ -0,0 +1,41 @@ +# residual.py + +from flux.factory import FluxCalculatorFactory +from reconstructor import ReconstructorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + + #self.reconstructor = ReconstructorFactory.create(self.config, self.domain) + self.reconstructor = ReconstructorFactory.create(cfd) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._compute_face_values() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _compute_face_values(self): + """私有方法:界面值重建""" + self.reconstructor.compute_face_values(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/result.py b/example/1d-linear-convection/weno3/python-v1/02c/src/result.py new file mode 100644 index 00000000..5e572216 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/result.py @@ -0,0 +1,25 @@ +# result.py + +class ResultAssembler: + @staticmethod + def assemble(cfd: 'Cfd') -> dict: + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied].copy() + + # ✅ 从 problem 获取解析解(唯一正确路径) + try: + analytical = cfd.problem.exact_solution(cfd) + except NotImplementedError: + analytical = None # 或 np.full_like(u_numerical, np.nan) + + return { + "x": cfd.domain.mesh.xcc, + "numerical": u_numerical, + "analytical": analytical, + "config": { + "scheme": cfd.config.recon_scheme, + "order": cfd.config.spatial_order, + "rk_order": cfd.config.rk_order, + "final_time": cfd.config.final_time, + "problem": cfd.problem.name, + } + } \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/solution.py b/example/1d-linear-convection/weno3/python-v1/02c/src/solution.py new file mode 100644 index 00000000..90666c34 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/solution.py @@ -0,0 +1,32 @@ +# solution.py +import numpy as np + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/solver.py b/example/1d-linear-convection/weno3/python-v1/02c/src/solver.py new file mode 100644 index 00000000..66b1ad40 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/solver.py @@ -0,0 +1,67 @@ +# solver.py + +from mesh import Mesh +from domain import Domain +from solution import Solution +from config import CfdConfig +from result import ResultAssembler +from problem import Problem +from initial_condition import InitialConditionFactory +from boundary.factory import BoundaryConditionFactory +from residual import ResidualCalculator +from time_integration import TimeIntegrator,TimeIntegratorFactory + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, problem: Problem, mesh): + self.problem = problem + self.config = problem.config + self.domain = Domain(problem.config, mesh) + self.solution = Solution(problem.config, self.domain) + + self.initial_condition = InitialConditionFactory.create(self.config) + self.boundary_condition = BoundaryConditionFactory.create(self) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + + # ==================== 公共接口(供 TimeIntegrator 调用) ==================== + def compute_residual(self): + """计算物理残差(封装重建→通量→散度)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件""" + self.boundary_condition.apply(self.solution.u) + + # ==================== 初始化 ==================== + def initialize(self): + """初始化全场:先 IC,再 BC,最后同步 old field""" + self.initial_condition.apply(self.solution) + # 应用边界条件到初始场 + self.apply_boundary() + # 同步 old field + self.solution.update_old_field() + + + def run(self): + self.initialize() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + self.result = ResultAssembler.assemble(self) + return self.result["numerical"] + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/time_integration/__init__.py b/example/1d-linear-convection/weno3/python-v1/02c/src/time_integration/__init__.py new file mode 100644 index 00000000..6342de3b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/time_integration/__init__.py @@ -0,0 +1,8 @@ +# time_integration/__init__.py + +# 导出统一接口 +from .factory import TimeIntegratorFactory +from .base import TimeIntegrator + +# 触发子模块注册(关键!) +from . import rk1, rk2, rk3 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/time_integration/base.py b/example/1d-linear-convection/weno3/python-v1/02c/src/time_integration/base.py new file mode 100644 index 00000000..0cdf29a4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/time_integration/base.py @@ -0,0 +1,27 @@ +# time_integration/base.py + +from abc import ABC, abstractmethod + +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + + @abstractmethod + def step(self, dt): + pass + + def compute_residual(self): + """计算残差(委托给 Cfd)""" + self.cfd.compute_residual() + + def apply_boundary(self): + """应用边界条件(委托给 Cfd)""" + self.cfd.apply_boundary() + + def map_idx(self, i): + """物理网格索引 → 残差数组索引""" + return i - self.domain.ist \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/time_integration/factory.py b/example/1d-linear-convection/weno3/python-v1/02c/src/time_integration/factory.py new file mode 100644 index 00000000..94662db2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/time_integration/factory.py @@ -0,0 +1,10 @@ +# time_integration/factory.py + +from core.registry import BaseFactory + +class TimeIntegratorFactory: + @staticmethod + def create(cfd) -> 'TimeIntegrator': + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/time_integration/rk1.py b/example/1d-linear-convection/weno3/python-v1/02c/src/time_integration/rk1.py new file mode 100644 index 00000000..b4c8a021 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/time_integration/rk1.py @@ -0,0 +1,15 @@ +# time_integration/rk1.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/time_integration/rk2.py b/example/1d-linear-convection/weno3/python-v1/02c/src/time_integration/rk2.py new file mode 100644 index 00000000..6d2be304 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/time_integration/rk2.py @@ -0,0 +1,29 @@ +# time_integration/rk2.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + 0.5 * self.solution.un[i] + + 0.5 * self.solution.u[i] + + 0.5 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02c/src/time_integration/rk3.py b/example/1d-linear-convection/weno3/python-v1/02c/src/time_integration/rk3.py new file mode 100644 index 00000000..c70791e1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02c/src/time_integration/rk3.py @@ -0,0 +1,43 @@ +# time_integration/rk3.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # Stage 1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # Stage 2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = ( + 0.75 * self.solution.un[i] + + 0.25 * self.solution.u[i] + + 0.25 * dt * self.solution.res[j] + ) + self.solution.u[:] = u2 + self.apply_boundary() + + # Stage 3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + c1 * self.solution.un[i] + + c2 * self.solution.u[i] + + c3 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/example/run_eno_weno.py b/example/1d-linear-convection/weno3/python-v1/02d/example/run_eno_weno.py new file mode 100644 index 00000000..c4063cb7 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/example/run_eno_weno.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +# example/run_eno_weno.py + +import sys +import os +src_path = os.path.join(os.path.dirname(__file__), '..', 'src') +if src_path not in sys.path: + sys.path.insert(0, src_path) + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +from problems.linear_advection import LinearAdvectionProblem + + +def performEnoWenoAnalysisBAK(): + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + print("Running ENO3 solver...") + config_eno3 = CfdConfig() + config_eno3.with_reconstruction("eno", 3) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno3 = LinearAdvectionProblem(config_eno3) + cfd_eno3 = Cfd(problem_eno3, mesh) # ← 注入 problem + cfd_eno3.run() + + # 3. 配置并运行 WENO3 求解 + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno3 = LinearAdvectionProblem(config_weno3) + cfd_weno3 = Cfd(problem_weno3, mesh) # ← 注入 problem + cfd_weno3.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" + ) + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO5 求解 + print("Running ENO5 solver...") + config_eno5 = CfdConfig() + config_eno5.with_reconstruction("eno", 5) + config_eno5.dt = 0.0025 + config_eno5.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno5 = LinearAdvectionProblem(config_eno5) + cfd_eno5 = Cfd(problem_eno5, mesh) # ← 注入 problem + cfd_eno5.run() + + # 3. 配置并运行 WENO5求解 + print("Running WENO5 solver...") + config_weno5 = CfdConfig() + config_weno5.with_reconstruction("weno", 5) + config_weno5.dt = 0.0025 + config_weno5.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno5 = LinearAdvectionProblem(config_weno5) + cfd_weno5 = Cfd(problem_weno5, mesh) # ← 注入 problem + cfd_weno5.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno5.result, + weno_result=cfd_weno5.result, + save_path="eno_weno_comparison.png" + ) + + +if __name__ == "__main__": + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/boundary/__init__.py b/example/1d-linear-convection/weno3/python-v1/02d/src/boundary/__init__.py new file mode 100644 index 00000000..20120e8a --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/boundary/__init__.py @@ -0,0 +1,21 @@ +# src/boundary/__init__.py +""" +边界条件模块 +提供各种边界条件实现和工厂创建接口 +""" + +from .base import BoundaryCondition +from .factory import BoundaryConditionFactory + +# 导入具体类以触发注册 +from .periodic import PeriodicBoundary +from .dirichlet import DirichletBoundary +from .neumann import NeumannBoundary + +__all__ = [ + 'BoundaryCondition', + 'BoundaryConditionFactory', + 'PeriodicBoundary', + 'DirichletBoundary', + 'NeumannBoundary', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/boundary/base.py b/example/1d-linear-convection/weno3/python-v1/02d/src/boundary/base.py new file mode 100644 index 00000000..cddf649d --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/boundary/base.py @@ -0,0 +1,18 @@ +# src/boundary/base.py +from abc import ABC, abstractmethod + +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/boundary/dirichlet.py b/example/1d-linear-convection/weno3/python-v1/02d/src/boundary/dirichlet.py new file mode 100644 index 00000000..1eff3699 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/boundary/dirichlet.py @@ -0,0 +1,27 @@ +# src/boundary/dirichlet.py +from .base import BoundaryCondition +from core.registry import register_component + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/boundary/factory.py b/example/1d-linear-convection/weno3/python-v1/02d/src/boundary/factory.py new file mode 100644 index 00000000..4bd7ae55 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/boundary/factory.py @@ -0,0 +1,24 @@ +# src/boundary/factory.py +from core.registry import BaseFactory + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + + @staticmethod + def get_available_types(): + """获取所有可用的边界条件类型""" + return BaseFactory.get_available_components('boundary') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/boundary/neumann.py b/example/1d-linear-convection/weno3/python-v1/02d/src/boundary/neumann.py new file mode 100644 index 00000000..6eb72f64 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/boundary/neumann.py @@ -0,0 +1,23 @@ +# src/boundary/neumann.py +from .base import BoundaryCondition +from core.registry import register_component + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/boundary/periodic.py b/example/1d-linear-convection/weno3/python-v1/02d/src/boundary/periodic.py new file mode 100644 index 00000000..bd601d95 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/boundary/periodic.py @@ -0,0 +1,19 @@ +# src/boundary/periodic.py +from .base import BoundaryCondition +from core.registry import register_component + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/config.py b/example/1d-linear-convection/weno3/python-v1/02d/src/config.py new file mode 100644 index 00000000..6432c80e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/config.py @@ -0,0 +1,45 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + #self.ic_type = "sin" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # "rusanov", "engquist-osher" + #self.flux_type = "engquist-osher" # "rusanov", "engquist-osher" + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + print(f"scheme={scheme}") + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/core/registry.py b/example/1d-linear-convection/weno3/python-v1/02d/src/core/registry.py new file mode 100644 index 00000000..4a52afc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/core/registry.py @@ -0,0 +1,83 @@ +# core/registry.py +""" +统一注册系统 + 通用工厂 +- ComponentRegistry: 组件注册表 +- register_component: 装饰器 +- BaseFactory: 通用工厂类 +""" + +from typing import Dict, Type, Any + + +# ==================== 1. 注册表核心 ==================== +class ComponentRegistry: + """组件注册表""" + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True + + @classmethod + def set_verbose(cls, verbose: bool): + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + #print(f"ComponentRegistry register cls={cls}") + #print(f"ComponentRegistry register name={name}") + #print(f"ComponentRegistry register component_class={component_class}") + if category not in cls._registries: + cls._registries[category] = {} + if name in cls._registries[category]: + if cls._registries[category][name] != component_class and cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + if category not in cls._registries: + raise ValueError(f"❌ 未知类别: {category} (可用: {list(cls._registries.keys())})") + if name not in cls._registries[category]: + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {list(cls._registries[category].keys())})") + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) for cat, comps in cls._registries.items()} + + +# ==================== 2. 装饰器 ==================== +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + #print(f"register_component decorator name={name}") + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + +# ==================== 3. 通用工厂 ==================== +class BaseFactory: + """通用工厂基类""" + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + name_lower = name.lower() + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + available = ComponentRegistry.list_all().get(category, []) + if available: + error_msg = f"不支持的 {category} 类型 '{name}'。可用类型:{available}" + else: + error_msg = f"不支持的 {category} 类型 '{name}'(无已注册组件)" + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/domain.py b/example/1d-linear-convection/weno3/python-v1/02d/src/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/equations/base.py b/example/1d-linear-convection/weno3/python-v1/02d/src/equations/base.py new file mode 100644 index 00000000..92e54f12 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/equations/base.py @@ -0,0 +1,56 @@ +# equations/base.py + +from abc import ABC, abstractmethod +import numpy as np + +class Equation(ABC): + """ + 控制方程抽象基类 + 所有物理方程(线性对流、Euler、MHD)必须继承此类 + """ + + @property + @abstractmethod + def num_equations(self) -> int: + """返回方程组变量数(标量=1,Euler=3,MHD=8)""" + pass + + @abstractmethod + def flux(self, u): + """ + 计算通量函数 f(u) + :param u: 状态向量 (num_equations,) + :return: 通量向量 (num_equations,) + """ + pass + + @abstractmethod + def max_wave_speed(self, u): + """ + 计算最大波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + @abstractmethod + def wave_speed(self): + """ + 计算波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + def exact_solution(self, x, t, initial_condition_func): + """ + 可选:提供解析解 + 子类可重写;若无解析解,调用方应捕获 NotImplementedError + :param x: 空间坐标数组 (ncells,) + :param t: 时间 + :param initial_condition_func: 初值函数 u0(x) -> (num_equations, ncells) + :return: 解 u(x,t) -> (num_equations, ncells) + """ + raise NotImplementedError( + f"Equation '{self.__class__.__name__}' does not provide an exact solution." + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/equations/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/02d/src/equations/linear_advection.py new file mode 100644 index 00000000..26aa026b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/equations/linear_advection.py @@ -0,0 +1,42 @@ +# equations/linear_advection.py + +import numpy as np +from .base import Equation + +class LinearAdvectionEquation(Equation): + """ + 线性对流方程: u_t + c * u_x = 0 + 支持标量(num_equations=1) + """ + + def __init__(self, wave_speed: float): + self.c = wave_speed + + @property + def num_equations(self) -> int: + return 1 + + def flux(self, u): + """f(u) = c * u""" + return self.c * u + + def max_wave_speed(self, u): + """最大波速 = |c|""" + return abs(self.c) + + def wave_speed(self): + return self.c + + + def exact_solution(self, x, t, initial_condition_func): + """ + 解析解: u(x, t) = u0(x - c * t) + 支持周期边界 + """ + if len(x) == 0: + return np.zeros((1, 0)) + + L = x[-1] - x[0] # 假设均匀周期网格 + x_shifted = (x - self.c * t + L) % L + u0 = initial_condition_func(x_shifted) # (1, ncells) + return u0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/initial_condition/__init__.py b/example/1d-linear-convection/weno3/python-v1/02d/src/initial_condition/__init__.py new file mode 100644 index 00000000..9eab71d5 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/initial_condition/__init__.py @@ -0,0 +1,12 @@ +# src/initial_conditions/__init__.py +""" +初始条件模块 +提供各种初始条件实现和工厂创建接口 +""" + +from .base import InitialCondition +from .factory import InitialConditionFactory + +from .step import StepFunctionIC +from .sine import SineWaveIC +from .gaussian import GaussianPulseIC diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/initial_condition/base.py b/example/1d-linear-convection/weno3/python-v1/02d/src/initial_condition/base.py new file mode 100644 index 00000000..8a33a0d5 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/initial_condition/base.py @@ -0,0 +1,25 @@ +# src/initial_conditions/base.py +from abc import ABC, abstractmethod +import numpy as np + +class InitialCondition(ABC): + """初始条件抽象基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + """内部辅助方法:将值应用到物理区域""" + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/initial_condition/factory.py b/example/1d-linear-convection/weno3/python-v1/02d/src/initial_condition/factory.py new file mode 100644 index 00000000..2a7558b2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/initial_condition/factory.py @@ -0,0 +1,24 @@ +# src/initial_conditions/factory.py +from core.registry import BaseFactory +from .base import InitialCondition + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + config: 配置对象,包含ic_type属性 + + Returns: + 初始条件实例 + """ + return BaseFactory.create_component('initial_condition', config.ic_type, config) + + @classmethod + def get_available_types(cls): + """获取所有可用的初始条件类型""" + return BaseFactory.get_available_components('initial_condition') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/initial_condition/gaussian.py b/example/1d-linear-convection/weno3/python-v1/02d/src/initial_condition/gaussian.py new file mode 100644 index 00000000..1dad463b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/initial_condition/gaussian.py @@ -0,0 +1,16 @@ +# src/initial_conditions/gaussian.py +import numpy as np +from .base import InitialCondition +from core.registry import register_component + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/initial_condition/sine.py b/example/1d-linear-convection/weno3/python-v1/02d/src/initial_condition/sine.py new file mode 100644 index 00000000..c061fa5a --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/initial_condition/sine.py @@ -0,0 +1,15 @@ +# src/initial_conditions/sine.py +import numpy as np +from .base import InitialCondition +from core.registry import register_component + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/initial_condition/step.py b/example/1d-linear-convection/weno3/python-v1/02d/src/initial_condition/step.py new file mode 100644 index 00000000..ff1120b2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/initial_condition/step.py @@ -0,0 +1,17 @@ +# src/initial_conditions/step.py +import numpy as np +from .base import InitialCondition +from core.registry import register_component + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/mesh.py b/example/1d-linear-convection/weno3/python-v1/02d/src/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/__init__.py b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/__init__.py new file mode 100644 index 00000000..4cfdcc73 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/__init__.py @@ -0,0 +1,27 @@ +# src/numerics/__init__.py +""" +数值方法模块 +包含重构器、通量计算、时间积分等数值方法 +""" + +from .residual import ResidualCalculator +from .reconstructor import Reconstructor, ReconstructorFactory +from .flux import InviscidFluxCalculator, FluxCalculatorFactory +from .time_integration import TimeIntegrator, TimeIntegratorFactory + +__all__ = [ + # 残差计算 + 'ResidualCalculator', + + # 重构器 + 'Reconstructor', + 'ReconstructorFactory', + + # 通量计算 + 'InviscidFluxCalculator', + 'FluxCalculatorFactory', + + # 时间积分 + 'TimeIntegrator', + 'TimeIntegratorFactory', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/flux/__init__.py b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/flux/__init__.py new file mode 100644 index 00000000..c39d6253 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/flux/__init__.py @@ -0,0 +1,19 @@ +# src/numerics/flux/__init__.py +""" +通量计算模块 +提供Rusanov、Engquist-Osher等通量计算方法 +""" + +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 导入具体实现以触发注册 +from .rusanov import RusanovFluxCalculator +from .engquist_osher import EngquistOsherFluxCalculator + +__all__ = [ + 'InviscidFluxCalculator', + 'FluxCalculatorFactory', + 'RusanovFluxCalculator', + 'EngquistOsherFluxCalculator', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/flux/base.py b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/flux/engquist_osher.py new file mode 100644 index 00000000..eb7b8cdf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/flux/engquist_osher.py @@ -0,0 +1,20 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + eq = self.cfd.problem.equation + for i in range(self.mesh.nnodes): + c = eq.wave_speed() + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/flux/factory.py b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/flux/factory.py new file mode 100644 index 00000000..c35f6e96 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/flux/factory.py @@ -0,0 +1,26 @@ +# src/numerics/flux/factory.py +from core.registry import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + + @staticmethod + def get_available_types(): + """获取所有可用的重构器类型""" + from core.registry import BaseFactory + return BaseFactory.get_available_components('flux') + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/flux/rusanov.py b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/flux/rusanov.py new file mode 100644 index 00000000..baa7c62c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/flux/rusanov.py @@ -0,0 +1,25 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量,使用 Equation 解耦物理参数""" + + def compute(self, q_face_left, q_face_right, flux): + # 从 cfd 获取 equation(与 Julia 对齐) + eq = self.cfd.problem.equation + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = eq.wave_speed() + c_R = eq.wave_speed() + # 通过 equation 计算通量和波速 + F_L = eq.flux(u_L) + F_R = eq.flux(u_R) + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/reconstructor/__init__.py new file mode 100644 index 00000000..6b0a3d9b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/reconstructor/__init__.py @@ -0,0 +1,21 @@ +# src/numerics/reconstructor/__init__.py +""" +数值重构模块 +提供ENO、WENO等界面值重构方法 +""" + +from .base import Reconstructor +from .factory import ReconstructorFactory + +# 导入具体实现以触发注册 +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from .weno5 import Weno5Reconstructor + +__all__ = [ + 'Reconstructor', + 'ReconstructorFactory', + 'EnoReconstructor', + 'Weno3Reconstructor', + 'Weno5Reconstructor', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/reconstructor/base.py b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/reconstructor/base.py new file mode 100644 index 00000000..de00327c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def compute_face_values(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/reconstructor/eno.py b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/reconstructor/eno.py new file mode 100644 index 00000000..60699adf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/reconstructor/eno.py @@ -0,0 +1,96 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.initialize(self.config.spatial_order, self.domain.ntcells) + + def initialize(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def compute_face_values(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/reconstructor/factory.py b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/reconstructor/factory.py new file mode 100644 index 00000000..1f297eaa --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/reconstructor/factory.py @@ -0,0 +1,20 @@ +# src/numerics/reconstructor/factory.py +from core.registry import BaseFactory + +class ReconstructorFactory: + @staticmethod + def create(cfd): + config = cfd.config + scheme = config.recon_scheme.lower() + + if scheme.startswith("weno"): + if scheme == "weno": + order = getattr(config, 'spatial_order', None) + scheme = f"weno{order}" + return BaseFactory.create_component('reconstructor', scheme, cfd) + + @staticmethod + def get_available_types(): + """获取所有可用的重构器类型""" + from core.registry import BaseFactory + return BaseFactory.get_available_components('reconstructor') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/reconstructor/weno3.py new file mode 100644 index 00000000..f45eb35f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/reconstructor/weno3.py @@ -0,0 +1,62 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def __init__(self, cfd): + self.cfd = cfd + + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/reconstructor/weno5.py b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/reconstructor/weno5.py new file mode 100644 index 00000000..9d665275 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/reconstructor/weno5.py @@ -0,0 +1,71 @@ +# reconstructor/weno5.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno5') +class Weno5Reconstructor(Reconstructor): + def __init__(self, cfd): + self.cfd = cfd + + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3, v4, v5) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3, v4, v5) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 1.0/10.0 + d1 = 3.0/5.0 + d2 = 3.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = 1.0/3.0*v1-7.0/6.0*v2+11.0/6.0*v3 # r=2 + q1 = -1.0/6.0*v2+5.0/6.0*v3+1.0/3.0*v4 # r=1 + q2 = 1.0/3.0*v3+5.0/6.0*v4-1.0/6.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 3.0/10.0 + d1 = 3.0/5.0 + d2 = 1.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = -1.0/6.0*v1+5.0/6.0*v2+1.0/3.0*v3 # r=2 + q1 = 1.0/3.0*v2+5.0/6.0*v3-1.0/6.0*v4 # r=1 + q2 = 11.0/6.0*v3-7.0/6.0*v4+1.0/3.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/residual.py b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/residual.py new file mode 100644 index 00000000..2f96f05b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/residual.py @@ -0,0 +1,39 @@ +# src/numerics/residual.py +from numerics.flux.factory import FluxCalculatorFactory +from numerics.reconstructor.factory import ReconstructorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + + self.reconstructor = ReconstructorFactory.create(cfd) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._compute_face_values() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _compute_face_values(self): + """私有方法:界面值重建""" + self.reconstructor.compute_face_values(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/time_integration/__init__.py b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/time_integration/__init__.py new file mode 100644 index 00000000..9c127c77 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/time_integration/__init__.py @@ -0,0 +1,21 @@ +# src/numerics/time_integration/__init__.py +""" +时间积分模块 +提供显式Runge-Kutta方法 +""" + +from .base import TimeIntegrator +from .factory import TimeIntegratorFactory + +# 导入具体实现以触发注册 +from .rk1 import RK1Integrator +from .rk2 import RK2Integrator +from .rk3 import RK3Integrator + +__all__ = [ + 'TimeIntegrator', + 'TimeIntegratorFactory', + 'RK1Integrator', + 'RK2Integrator', + 'RK3Integrator', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/time_integration/base.py b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/time_integration/base.py new file mode 100644 index 00000000..0cdf29a4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/time_integration/base.py @@ -0,0 +1,27 @@ +# time_integration/base.py + +from abc import ABC, abstractmethod + +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + + @abstractmethod + def step(self, dt): + pass + + def compute_residual(self): + """计算残差(委托给 Cfd)""" + self.cfd.compute_residual() + + def apply_boundary(self): + """应用边界条件(委托给 Cfd)""" + self.cfd.apply_boundary() + + def map_idx(self, i): + """物理网格索引 → 残差数组索引""" + return i - self.domain.ist \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/time_integration/factory.py b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/time_integration/factory.py new file mode 100644 index 00000000..5e6f9779 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/time_integration/factory.py @@ -0,0 +1,15 @@ +# src/numerics/time_integration/factory.py +from core.registry import BaseFactory + +class TimeIntegratorFactory: + @staticmethod + def create(cfd) -> 'TimeIntegrator': + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) + + @staticmethod + def get_available_types(): + """获取所有可用的重构器类型""" + from core.registry import BaseFactory + return BaseFactory.get_available_components('integrator') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/time_integration/rk1.py b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/time_integration/rk1.py new file mode 100644 index 00000000..b4c8a021 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/time_integration/rk1.py @@ -0,0 +1,15 @@ +# time_integration/rk1.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/time_integration/rk2.py b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/time_integration/rk2.py new file mode 100644 index 00000000..6d2be304 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/time_integration/rk2.py @@ -0,0 +1,29 @@ +# time_integration/rk2.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + 0.5 * self.solution.un[i] + + 0.5 * self.solution.u[i] + + 0.5 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/time_integration/rk3.py b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/time_integration/rk3.py new file mode 100644 index 00000000..c70791e1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/numerics/time_integration/rk3.py @@ -0,0 +1,43 @@ +# time_integration/rk3.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # Stage 1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # Stage 2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = ( + 0.75 * self.solution.un[i] + + 0.25 * self.solution.u[i] + + 0.25 * dt * self.solution.res[j] + ) + self.solution.u[:] = u2 + self.apply_boundary() + + # Stage 3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + c1 * self.solution.un[i] + + c2 * self.solution.u[i] + + c3 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/plotter.py b/example/1d-linear-convection/weno3/python-v1/02d/src/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/problem.py b/example/1d-linear-convection/weno3/python-v1/02d/src/problem.py new file mode 100644 index 00000000..53719f26 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/problem.py @@ -0,0 +1,38 @@ +# problem.py +""" +问题定义模块:每个 Problem 子类代表一个完整测试用例 +包含初始条件、解析解(可选)、物理方程等 +""" + +from abc import ABC, abstractmethod +from initial_condition import InitialConditionFactory + + +class Problem(ABC): + """ + 抽象问题基类 + 每个具体问题(如线性对流、Sod 激波管)应继承此类 + """ + def __init__(self, config): + self.config = config + + @abstractmethod + def initial_condition(self): + """ + 返回 InitialCondition 实例 + """ + pass + + def exact_solution(self, cfd): + """ + 可选:返回解析解(数值数组) + 若无解析解,可抛出 NotImplementedError 或返回 None + """ + x = cfd.domain.mesh.xcc + raise NotImplementedError( + f"Problem '{self.__class__.__name__}' does not provide an exact solution." + ) + + @property + def name(self): + return self.__class__.__name__ \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/problems/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/02d/src/problems/linear_advection.py new file mode 100644 index 00000000..b6d548f5 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/problems/linear_advection.py @@ -0,0 +1,39 @@ +# problems/linear_advection.py + +import numpy as np +from problem import Problem +from equations.linear_advection import LinearAdvectionEquation +from initial_condition import InitialConditionFactory + +class LinearAdvectionProblem(Problem): + """ + 线性对流问题:u_t + c u_x = 0 + 使用周期边界 + 任意初始条件 + """ + + def __init__(self, config): + super().__init__(config) + # 持有物理方程实例 + self.equation = LinearAdvectionEquation(wave_speed=config.wave_speed) + self._ic_cache = None # 缓存 IC 实例 + + def initial_condition(self): + """返回初始条件实例""" + if self._ic_cache is None: + self._ic_cache = InitialConditionFactory.create(self.config) + return self._ic_cache + + def exact_solution(self, cfd): + """ + 委托给 Equation 计算解析解 + """ + ic = self.initial_condition() + # 包装 evaluate_at 以返回 (1, ncells) + def vectorized_ic(x): + u0_scalar = ic.evaluate_at(x) # (ncells,) + return u0_scalar[np.newaxis, :] # (1, ncells) + + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + u_exact = self.equation.exact_solution(x, t, vectorized_ic) + return u_exact[0] # 返回标量数组 (ncells,) 以兼容现有绘图逻辑 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/result.py b/example/1d-linear-convection/weno3/python-v1/02d/src/result.py new file mode 100644 index 00000000..5e572216 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/result.py @@ -0,0 +1,25 @@ +# result.py + +class ResultAssembler: + @staticmethod + def assemble(cfd: 'Cfd') -> dict: + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied].copy() + + # ✅ 从 problem 获取解析解(唯一正确路径) + try: + analytical = cfd.problem.exact_solution(cfd) + except NotImplementedError: + analytical = None # 或 np.full_like(u_numerical, np.nan) + + return { + "x": cfd.domain.mesh.xcc, + "numerical": u_numerical, + "analytical": analytical, + "config": { + "scheme": cfd.config.recon_scheme, + "order": cfd.config.spatial_order, + "rk_order": cfd.config.rk_order, + "final_time": cfd.config.final_time, + "problem": cfd.problem.name, + } + } \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/solution.py b/example/1d-linear-convection/weno3/python-v1/02d/src/solution.py new file mode 100644 index 00000000..90666c34 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/solution.py @@ -0,0 +1,32 @@ +# solution.py +import numpy as np + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02d/src/solver.py b/example/1d-linear-convection/weno3/python-v1/02d/src/solver.py new file mode 100644 index 00000000..33e3fb75 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02d/src/solver.py @@ -0,0 +1,68 @@ +# solver.py + +from mesh import Mesh +from domain import Domain +from solution import Solution +from config import CfdConfig +from result import ResultAssembler +from problem import Problem +from initial_condition import InitialConditionFactory +from boundary.factory import BoundaryConditionFactory + +from numerics.time_integration.factory import TimeIntegratorFactory +from numerics.residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, problem: Problem, mesh): + self.problem = problem + self.config = problem.config + self.domain = Domain(problem.config, mesh) + self.solution = Solution(problem.config, self.domain) + + self.initial_condition = InitialConditionFactory.create(self.config) + self.boundary_condition = BoundaryConditionFactory.create(self) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + + # ==================== 公共接口(供 TimeIntegrator 调用) ==================== + def compute_residual(self): + """计算物理残差(封装重建→通量→散度)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件""" + self.boundary_condition.apply(self.solution.u) + + # ==================== 初始化 ==================== + def initialize(self): + """初始化全场:先 IC,再 BC,最后同步 old field""" + self.initial_condition.apply(self.solution) + # 应用边界条件到初始场 + self.apply_boundary() + # 同步 old field + self.solution.update_old_field() + + + def run(self): + self.initialize() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + self.result = ResultAssembler.assemble(self) + return self.result["numerical"] + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/example/run_eno_weno.py b/example/1d-linear-convection/weno3/python-v1/02e/example/run_eno_weno.py new file mode 100644 index 00000000..599d7652 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/example/run_eno_weno.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +# example/run_eno_weno.py + +import sys +import os +print(f"当前工作目录: {os.getcwd()}") +print(f"Python路径:") +for path in sys.path: + print(f" {path}") + +src_path = os.path.join(os.path.dirname(__file__), '..', 'src') +print(f"\n尝试添加src路径: {src_path}") +print(f"src路径是否存在: {os.path.exists(src_path)}") +print(f"physics目录是否存在: {os.path.exists(os.path.join(src_path, 'physics'))}") +print(f"initial_conditions目录是否存在: {os.path.exists(os.path.join(src_path, 'physics', 'initial_conditions'))}") + +if src_path not in sys.path: + sys.path.insert(0, src_path) + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter +from physics.problems.linear_advection import LinearAdvectionProblem + + +def performEnoWenoAnalysisBAK(): + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + print("Running ENO3 solver...") + config_eno3 = CfdConfig() + config_eno3.with_reconstruction("eno", 3) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno3 = LinearAdvectionProblem(config_eno3) + cfd_eno3 = Cfd(problem_eno3, mesh) # ← 注入 problem + cfd_eno3.run() + + # 3. 配置并运行 WENO3 求解 + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno3 = LinearAdvectionProblem(config_weno3) + cfd_weno3 = Cfd(problem_weno3, mesh) # ← 注入 problem + cfd_weno3.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" + ) + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO5 求解 + print("Running ENO5 solver...") + config_eno5 = CfdConfig() + config_eno5.with_reconstruction("eno", 5) + config_eno5.dt = 0.0025 + config_eno5.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno5 = LinearAdvectionProblem(config_eno5) + cfd_eno5 = Cfd(problem_eno5, mesh) # ← 注入 problem + cfd_eno5.run() + + # 3. 配置并运行 WENO5求解 + print("Running WENO5 solver...") + config_weno5 = CfdConfig() + config_weno5.with_reconstruction("weno", 5) + config_weno5.dt = 0.0025 + config_weno5.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno5 = LinearAdvectionProblem(config_weno5) + cfd_weno5 = Cfd(problem_weno5, mesh) # ← 注入 problem + cfd_weno5.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno5.result, + weno_result=cfd_weno5.result, + save_path="eno_weno_comparison.png" + ) + + +if __name__ == "__main__": + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/boundary/__init__.py b/example/1d-linear-convection/weno3/python-v1/02e/src/boundary/__init__.py new file mode 100644 index 00000000..20120e8a --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/boundary/__init__.py @@ -0,0 +1,21 @@ +# src/boundary/__init__.py +""" +边界条件模块 +提供各种边界条件实现和工厂创建接口 +""" + +from .base import BoundaryCondition +from .factory import BoundaryConditionFactory + +# 导入具体类以触发注册 +from .periodic import PeriodicBoundary +from .dirichlet import DirichletBoundary +from .neumann import NeumannBoundary + +__all__ = [ + 'BoundaryCondition', + 'BoundaryConditionFactory', + 'PeriodicBoundary', + 'DirichletBoundary', + 'NeumannBoundary', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/boundary/base.py b/example/1d-linear-convection/weno3/python-v1/02e/src/boundary/base.py new file mode 100644 index 00000000..cddf649d --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/boundary/base.py @@ -0,0 +1,18 @@ +# src/boundary/base.py +from abc import ABC, abstractmethod + +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/boundary/dirichlet.py b/example/1d-linear-convection/weno3/python-v1/02e/src/boundary/dirichlet.py new file mode 100644 index 00000000..1eff3699 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/boundary/dirichlet.py @@ -0,0 +1,27 @@ +# src/boundary/dirichlet.py +from .base import BoundaryCondition +from core.registry import register_component + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/boundary/factory.py b/example/1d-linear-convection/weno3/python-v1/02e/src/boundary/factory.py new file mode 100644 index 00000000..4bd7ae55 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/boundary/factory.py @@ -0,0 +1,24 @@ +# src/boundary/factory.py +from core.registry import BaseFactory + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + + @staticmethod + def get_available_types(): + """获取所有可用的边界条件类型""" + return BaseFactory.get_available_components('boundary') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/boundary/neumann.py b/example/1d-linear-convection/weno3/python-v1/02e/src/boundary/neumann.py new file mode 100644 index 00000000..6eb72f64 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/boundary/neumann.py @@ -0,0 +1,23 @@ +# src/boundary/neumann.py +from .base import BoundaryCondition +from core.registry import register_component + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/boundary/periodic.py b/example/1d-linear-convection/weno3/python-v1/02e/src/boundary/periodic.py new file mode 100644 index 00000000..bd601d95 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/boundary/periodic.py @@ -0,0 +1,19 @@ +# src/boundary/periodic.py +from .base import BoundaryCondition +from core.registry import register_component + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/config.py b/example/1d-linear-convection/weno3/python-v1/02e/src/config.py new file mode 100644 index 00000000..6432c80e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/config.py @@ -0,0 +1,45 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + #self.ic_type = "sin" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # "rusanov", "engquist-osher" + #self.flux_type = "engquist-osher" # "rusanov", "engquist-osher" + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + print(f"scheme={scheme}") + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/core/registry.py b/example/1d-linear-convection/weno3/python-v1/02e/src/core/registry.py new file mode 100644 index 00000000..4a52afc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/core/registry.py @@ -0,0 +1,83 @@ +# core/registry.py +""" +统一注册系统 + 通用工厂 +- ComponentRegistry: 组件注册表 +- register_component: 装饰器 +- BaseFactory: 通用工厂类 +""" + +from typing import Dict, Type, Any + + +# ==================== 1. 注册表核心 ==================== +class ComponentRegistry: + """组件注册表""" + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True + + @classmethod + def set_verbose(cls, verbose: bool): + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + #print(f"ComponentRegistry register cls={cls}") + #print(f"ComponentRegistry register name={name}") + #print(f"ComponentRegistry register component_class={component_class}") + if category not in cls._registries: + cls._registries[category] = {} + if name in cls._registries[category]: + if cls._registries[category][name] != component_class and cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + if category not in cls._registries: + raise ValueError(f"❌ 未知类别: {category} (可用: {list(cls._registries.keys())})") + if name not in cls._registries[category]: + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {list(cls._registries[category].keys())})") + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) for cat, comps in cls._registries.items()} + + +# ==================== 2. 装饰器 ==================== +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + #print(f"register_component decorator name={name}") + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + +# ==================== 3. 通用工厂 ==================== +class BaseFactory: + """通用工厂基类""" + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + name_lower = name.lower() + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + available = ComponentRegistry.list_all().get(category, []) + if available: + error_msg = f"不支持的 {category} 类型 '{name}'。可用类型:{available}" + else: + error_msg = f"不支持的 {category} 类型 '{name}'(无已注册组件)" + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/domain.py b/example/1d-linear-convection/weno3/python-v1/02e/src/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/mesh.py b/example/1d-linear-convection/weno3/python-v1/02e/src/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/__init__.py b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/__init__.py new file mode 100644 index 00000000..4cfdcc73 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/__init__.py @@ -0,0 +1,27 @@ +# src/numerics/__init__.py +""" +数值方法模块 +包含重构器、通量计算、时间积分等数值方法 +""" + +from .residual import ResidualCalculator +from .reconstructor import Reconstructor, ReconstructorFactory +from .flux import InviscidFluxCalculator, FluxCalculatorFactory +from .time_integration import TimeIntegrator, TimeIntegratorFactory + +__all__ = [ + # 残差计算 + 'ResidualCalculator', + + # 重构器 + 'Reconstructor', + 'ReconstructorFactory', + + # 通量计算 + 'InviscidFluxCalculator', + 'FluxCalculatorFactory', + + # 时间积分 + 'TimeIntegrator', + 'TimeIntegratorFactory', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/flux/__init__.py b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/flux/__init__.py new file mode 100644 index 00000000..c39d6253 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/flux/__init__.py @@ -0,0 +1,19 @@ +# src/numerics/flux/__init__.py +""" +通量计算模块 +提供Rusanov、Engquist-Osher等通量计算方法 +""" + +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 导入具体实现以触发注册 +from .rusanov import RusanovFluxCalculator +from .engquist_osher import EngquistOsherFluxCalculator + +__all__ = [ + 'InviscidFluxCalculator', + 'FluxCalculatorFactory', + 'RusanovFluxCalculator', + 'EngquistOsherFluxCalculator', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/flux/base.py b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/flux/engquist_osher.py new file mode 100644 index 00000000..16dbce82 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/flux/engquist_osher.py @@ -0,0 +1,20 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + eq = self.cfd.problem.physical_system().equation + for i in range(self.mesh.nnodes): + c = eq.wave_speed() + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/flux/factory.py b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/flux/factory.py new file mode 100644 index 00000000..c35f6e96 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/flux/factory.py @@ -0,0 +1,26 @@ +# src/numerics/flux/factory.py +from core.registry import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + + @staticmethod + def get_available_types(): + """获取所有可用的重构器类型""" + from core.registry import BaseFactory + return BaseFactory.get_available_components('flux') + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/flux/rusanov.py b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/flux/rusanov.py new file mode 100644 index 00000000..56daa699 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/flux/rusanov.py @@ -0,0 +1,25 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量,使用 Equation 解耦物理参数""" + + def compute(self, q_face_left, q_face_right, flux): + # 从 cfd 获取 equation(与 Julia 对齐) + eq = self.cfd.problem.physical_system().equation + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = eq.wave_speed() + c_R = eq.wave_speed() + # 通过 equation 计算通量和波速 + F_L = eq.flux(u_L) + F_R = eq.flux(u_R) + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/reconstructor/__init__.py new file mode 100644 index 00000000..6b0a3d9b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/reconstructor/__init__.py @@ -0,0 +1,21 @@ +# src/numerics/reconstructor/__init__.py +""" +数值重构模块 +提供ENO、WENO等界面值重构方法 +""" + +from .base import Reconstructor +from .factory import ReconstructorFactory + +# 导入具体实现以触发注册 +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from .weno5 import Weno5Reconstructor + +__all__ = [ + 'Reconstructor', + 'ReconstructorFactory', + 'EnoReconstructor', + 'Weno3Reconstructor', + 'Weno5Reconstructor', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/reconstructor/base.py b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/reconstructor/base.py new file mode 100644 index 00000000..de00327c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def compute_face_values(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/reconstructor/eno.py b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/reconstructor/eno.py new file mode 100644 index 00000000..60699adf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/reconstructor/eno.py @@ -0,0 +1,96 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.initialize(self.config.spatial_order, self.domain.ntcells) + + def initialize(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def compute_face_values(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/reconstructor/factory.py b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/reconstructor/factory.py new file mode 100644 index 00000000..1f297eaa --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/reconstructor/factory.py @@ -0,0 +1,20 @@ +# src/numerics/reconstructor/factory.py +from core.registry import BaseFactory + +class ReconstructorFactory: + @staticmethod + def create(cfd): + config = cfd.config + scheme = config.recon_scheme.lower() + + if scheme.startswith("weno"): + if scheme == "weno": + order = getattr(config, 'spatial_order', None) + scheme = f"weno{order}" + return BaseFactory.create_component('reconstructor', scheme, cfd) + + @staticmethod + def get_available_types(): + """获取所有可用的重构器类型""" + from core.registry import BaseFactory + return BaseFactory.get_available_components('reconstructor') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/reconstructor/weno3.py new file mode 100644 index 00000000..f45eb35f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/reconstructor/weno3.py @@ -0,0 +1,62 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def __init__(self, cfd): + self.cfd = cfd + + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/reconstructor/weno5.py b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/reconstructor/weno5.py new file mode 100644 index 00000000..9d665275 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/reconstructor/weno5.py @@ -0,0 +1,71 @@ +# reconstructor/weno5.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno5') +class Weno5Reconstructor(Reconstructor): + def __init__(self, cfd): + self.cfd = cfd + + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3, v4, v5) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3, v4, v5) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 1.0/10.0 + d1 = 3.0/5.0 + d2 = 3.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = 1.0/3.0*v1-7.0/6.0*v2+11.0/6.0*v3 # r=2 + q1 = -1.0/6.0*v2+5.0/6.0*v3+1.0/3.0*v4 # r=1 + q2 = 1.0/3.0*v3+5.0/6.0*v4-1.0/6.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 3.0/10.0 + d1 = 3.0/5.0 + d2 = 1.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = -1.0/6.0*v1+5.0/6.0*v2+1.0/3.0*v3 # r=2 + q1 = 1.0/3.0*v2+5.0/6.0*v3-1.0/6.0*v4 # r=1 + q2 = 11.0/6.0*v3-7.0/6.0*v4+1.0/3.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/residual.py b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/residual.py new file mode 100644 index 00000000..2f96f05b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/residual.py @@ -0,0 +1,39 @@ +# src/numerics/residual.py +from numerics.flux.factory import FluxCalculatorFactory +from numerics.reconstructor.factory import ReconstructorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + + self.reconstructor = ReconstructorFactory.create(cfd) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._compute_face_values() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _compute_face_values(self): + """私有方法:界面值重建""" + self.reconstructor.compute_face_values(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/time_integration/__init__.py b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/time_integration/__init__.py new file mode 100644 index 00000000..9c127c77 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/time_integration/__init__.py @@ -0,0 +1,21 @@ +# src/numerics/time_integration/__init__.py +""" +时间积分模块 +提供显式Runge-Kutta方法 +""" + +from .base import TimeIntegrator +from .factory import TimeIntegratorFactory + +# 导入具体实现以触发注册 +from .rk1 import RK1Integrator +from .rk2 import RK2Integrator +from .rk3 import RK3Integrator + +__all__ = [ + 'TimeIntegrator', + 'TimeIntegratorFactory', + 'RK1Integrator', + 'RK2Integrator', + 'RK3Integrator', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/time_integration/base.py b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/time_integration/base.py new file mode 100644 index 00000000..0cdf29a4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/time_integration/base.py @@ -0,0 +1,27 @@ +# time_integration/base.py + +from abc import ABC, abstractmethod + +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + + @abstractmethod + def step(self, dt): + pass + + def compute_residual(self): + """计算残差(委托给 Cfd)""" + self.cfd.compute_residual() + + def apply_boundary(self): + """应用边界条件(委托给 Cfd)""" + self.cfd.apply_boundary() + + def map_idx(self, i): + """物理网格索引 → 残差数组索引""" + return i - self.domain.ist \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/time_integration/factory.py b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/time_integration/factory.py new file mode 100644 index 00000000..5e6f9779 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/time_integration/factory.py @@ -0,0 +1,15 @@ +# src/numerics/time_integration/factory.py +from core.registry import BaseFactory + +class TimeIntegratorFactory: + @staticmethod + def create(cfd) -> 'TimeIntegrator': + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) + + @staticmethod + def get_available_types(): + """获取所有可用的重构器类型""" + from core.registry import BaseFactory + return BaseFactory.get_available_components('integrator') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/time_integration/rk1.py b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/time_integration/rk1.py new file mode 100644 index 00000000..b4c8a021 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/time_integration/rk1.py @@ -0,0 +1,15 @@ +# time_integration/rk1.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/time_integration/rk2.py b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/time_integration/rk2.py new file mode 100644 index 00000000..6d2be304 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/time_integration/rk2.py @@ -0,0 +1,29 @@ +# time_integration/rk2.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + 0.5 * self.solution.un[i] + + 0.5 * self.solution.u[i] + + 0.5 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/time_integration/rk3.py b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/time_integration/rk3.py new file mode 100644 index 00000000..c70791e1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/numerics/time_integration/rk3.py @@ -0,0 +1,43 @@ +# time_integration/rk3.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # Stage 1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # Stage 2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = ( + 0.75 * self.solution.un[i] + + 0.25 * self.solution.u[i] + + 0.25 * dt * self.solution.res[j] + ) + self.solution.u[:] = u2 + self.apply_boundary() + + # Stage 3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + c1 * self.solution.un[i] + + c2 * self.solution.u[i] + + c3 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/physics/__init__.py b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/physics/equations/__init__.py b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/equations/__init__.py new file mode 100644 index 00000000..14b82117 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/equations/__init__.py @@ -0,0 +1,15 @@ +# src/physics/equations/__init__.py +""" +物理方程模块 +提供各种控制方程的实现 +""" + +from .base import Equation +from .linear_advection import LinearAdvectionEquation +from .euler import EulerEquation + +__all__ = [ + 'Equation', + 'LinearAdvectionEquation', + 'EulerEquation', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/physics/equations/base.py b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/equations/base.py new file mode 100644 index 00000000..05833e74 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/equations/base.py @@ -0,0 +1,60 @@ +# src/physics/equations/base.py +""" +物理方程抽象基类 +所有控制方程必须继承此类 +""" + +from abc import ABC, abstractmethod +import numpy as np + +class Equation(ABC): + """ + 控制方程抽象基类 + 所有物理方程(线性对流、Euler、MHD)必须继承此类 + """ + + @property + @abstractmethod + def num_equations(self) -> int: + """返回方程组变量数(标量=1,Euler=3,MHD=8)""" + pass + + @abstractmethod + def flux(self, u): + """ + 计算通量函数 f(u) + :param u: 状态向量 (num_equations,) + :return: 通量向量 (num_equations,) + """ + pass + + @abstractmethod + def max_wave_speed(self, u): + """ + 计算最大波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + @abstractmethod + def wave_speed(self): + """ + 计算波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + def exact_solution(self, x, t, initial_condition_func): + """ + 可选:提供解析解 + 子类可重写;若无解析解,调用方应捕获 NotImplementedError + :param x: 空间坐标数组 (ncells,) + :param t: 时间 + :param initial_condition_func: 初值函数 u0(x) -> (num_equations, ncells) + :return: 解 u(x,t) -> (num_equations, ncells) + """ + raise NotImplementedError( + f"Equation '{self.__class__.__name__}' does not provide an exact solution." + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/physics/equations/euler.py b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/equations/euler.py new file mode 100644 index 00000000..0b69c2d7 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/equations/euler.py @@ -0,0 +1,23 @@ +# src/physics/equations/euler.py +""" +欧拉方程(占位文件,未来实现) +""" +from .base import Equation + +class EulerEquation(Equation): + """欧拉方程:质量、动量、能量守恒""" + def __init__(self, gamma=1.4): + self.gamma = gamma + + @property + def num_equations(self) -> int: + return 3 + + def flux(self, u): + raise NotImplementedError("欧拉方程待实现") + + def max_wave_speed(self, u): + raise NotImplementedError("欧拉方程待实现") + + def wave_speed(self): + raise NotImplementedError("欧拉方程待实现") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/physics/equations/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/equations/linear_advection.py new file mode 100644 index 00000000..949c4a0a --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/equations/linear_advection.py @@ -0,0 +1,42 @@ +# src/physics/equations/linear_advection.py +""" +线性对流方程: u_t + c * u_x = 0 +支持标量(num_equations=1) +""" + +import numpy as np +from .base import Equation +from core.registry import register_component + +@register_component('equation', 'linear_advection') +class LinearAdvectionEquation(Equation): + def __init__(self, wave_speed: float): + self.c = wave_speed + + @property + def num_equations(self) -> int: + return 1 + + def flux(self, u): + """f(u) = c * u""" + return self.c * u + + def max_wave_speed(self, u): + """最大波速 = |c|""" + return abs(self.c) + + def wave_speed(self): + return self.c + + def exact_solution(self, x, t, initial_condition_func): + """ + 解析解: u(x, t) = u0(x - c * t) + 支持周期边界 + """ + if len(x) == 0: + return np.zeros((1, 0)) + + L = x[-1] - x[0] # 假设均匀周期网格 + x_shifted = (x - self.c * t + L) % L + u0 = initial_condition_func(x_shifted) # (1, ncells) + return u0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/physics/initial_conditions/__init__.py b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/initial_conditions/__init__.py new file mode 100644 index 00000000..94b0ad80 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/initial_conditions/__init__.py @@ -0,0 +1,16 @@ +# src/physics/initial_conditions/__init__.py +from .base import InitialCondition +from .factory import InitialConditionFactory + +# 导入具体类以触发注册 +from .step import StepFunctionIC +from .sine import SineWaveIC +from .gaussian import GaussianPulseIC + +__all__ = [ + 'InitialCondition', + 'InitialConditionFactory', + 'StepFunctionIC', + 'SineWaveIC', + 'GaussianPulseIC', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/physics/initial_conditions/base.py b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/initial_conditions/base.py new file mode 100644 index 00000000..8a33a0d5 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/initial_conditions/base.py @@ -0,0 +1,25 @@ +# src/initial_conditions/base.py +from abc import ABC, abstractmethod +import numpy as np + +class InitialCondition(ABC): + """初始条件抽象基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + """内部辅助方法:将值应用到物理区域""" + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/physics/initial_conditions/factory.py b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/initial_conditions/factory.py new file mode 100644 index 00000000..2a7558b2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/initial_conditions/factory.py @@ -0,0 +1,24 @@ +# src/initial_conditions/factory.py +from core.registry import BaseFactory +from .base import InitialCondition + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + config: 配置对象,包含ic_type属性 + + Returns: + 初始条件实例 + """ + return BaseFactory.create_component('initial_condition', config.ic_type, config) + + @classmethod + def get_available_types(cls): + """获取所有可用的初始条件类型""" + return BaseFactory.get_available_components('initial_condition') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/physics/initial_conditions/gaussian.py b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/initial_conditions/gaussian.py new file mode 100644 index 00000000..1dad463b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/initial_conditions/gaussian.py @@ -0,0 +1,16 @@ +# src/initial_conditions/gaussian.py +import numpy as np +from .base import InitialCondition +from core.registry import register_component + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/physics/initial_conditions/sine.py b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/initial_conditions/sine.py new file mode 100644 index 00000000..c061fa5a --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/initial_conditions/sine.py @@ -0,0 +1,15 @@ +# src/initial_conditions/sine.py +import numpy as np +from .base import InitialCondition +from core.registry import register_component + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/physics/initial_conditions/step.py b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/initial_conditions/step.py new file mode 100644 index 00000000..ff1120b2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/initial_conditions/step.py @@ -0,0 +1,17 @@ +# src/initial_conditions/step.py +import numpy as np +from .base import InitialCondition +from core.registry import register_component + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/physics/problems/__init__.py b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/problems/__init__.py new file mode 100644 index 00000000..e9d32905 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/problems/__init__.py @@ -0,0 +1,14 @@ +# src/physics/problems/__init__.py +""" +问题定义模块 +""" + +from .base import Problem +from .factory import ProblemFactory +from .linear_advection import LinearAdvectionProblem + +__all__ = [ + 'Problem', + 'ProblemFactory', + 'LinearAdvectionProblem', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/physics/problems/base.py b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/problems/base.py new file mode 100644 index 00000000..ddd8ddc4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/problems/base.py @@ -0,0 +1,42 @@ +# src/physics/problems/base.py +""" +问题定义抽象基类 +每个具体问题(如线性对流、Sod 激波管)应继承此类 +""" + +from abc import ABC, abstractmethod + +class Problem(ABC): + """ + 抽象问题基类 + """ + def __init__(self, config): + self.config = config + self._initial_condition = None + self._physical_system = None + + @abstractmethod + def initial_condition(self): + """返回 InitialCondition 实例""" + pass + + @abstractmethod + def physical_system(self): + """返回 PhysicalSystem 实例""" + pass + + def exact_solution(self, cfd): + """ + 计算解析解(委托给物理系统) + """ + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + ic = self.initial_condition() + system = self.physical_system() + + u_exact = system.exact_solution(x, t, ic) + return u_exact[0] # 返回标量数组 (ncells,) 以兼容现有绘图逻辑 + + @property + def name(self): + return self.__class__.__name__ \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/physics/problems/factory.py b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/problems/factory.py new file mode 100644 index 00000000..b3e3563f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/problems/factory.py @@ -0,0 +1,28 @@ +# src/physics/problems/factory.py +""" +问题工厂 +""" + +from core.registry import BaseFactory + +class ProblemFactory: + """问题工厂""" + + @staticmethod + def create(problem_type: str, config): + """ + 创建问题实例 + + Args: + problem_type: 问题类型,如 'linear_advection' + config: 配置对象 + + Returns: + 问题实例 + """ + return BaseFactory.create_component('problem', problem_type, config) + + @staticmethod + def get_available_types(): + """获取所有可用的问题类型""" + return BaseFactory.get_available_components('problem') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/physics/problems/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/problems/linear_advection.py new file mode 100644 index 00000000..f19ea53a --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/problems/linear_advection.py @@ -0,0 +1,36 @@ +# src/physics/problems/linear_advection.py +""" +线性对流问题 +""" + +from .base import Problem +from physics.equations.linear_advection import LinearAdvectionEquation +from physics.systems.linear_advection_system import LinearAdvectionSystem +from physics.initial_conditions.factory import InitialConditionFactory +from core.registry import register_component + +@register_component('problem', 'linear_advection') +class LinearAdvectionProblem(Problem): + """ + 线性对流问题:u_t + c u_x = 0 + 使用周期边界 + 任意初始条件 + """ + + def __init__(self, config): + super().__init__(config) + + def initial_condition(self): + """返回初始条件实例""" + if self._initial_condition is None: + from physics.initial_conditions.factory import InitialConditionFactory + self._initial_condition = InitialConditionFactory.create(self.config) + return self._initial_condition + + def physical_system(self): + """返回物理系统实例""" + if self._physical_system is None: + # 创建方程 + equation = LinearAdvectionEquation(wave_speed=self.config.wave_speed) + # 创建系统 + self._physical_system = LinearAdvectionSystem(equation) + return self._physical_system \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/physics/systems/__init__.py b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/systems/__init__.py new file mode 100644 index 00000000..bd22409c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/systems/__init__.py @@ -0,0 +1,15 @@ +# src/physics/systems/__init__.py +""" +物理系统模块 +组合方程和解析解逻辑 +""" + +from .base import PhysicalSystem +from .factory import PhysicalSystemFactory +from .linear_advection_system import LinearAdvectionSystem + +__all__ = [ + 'PhysicalSystem', + 'PhysicalSystemFactory', + 'LinearAdvectionSystem', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/physics/systems/base.py b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/systems/base.py new file mode 100644 index 00000000..957212d0 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/systems/base.py @@ -0,0 +1,33 @@ +# src/physics/systems/base.py +""" +物理系统基类 +将方程与解析解等逻辑组合 +""" + +from abc import ABC, abstractmethod + +class PhysicalSystem(ABC): + """物理系统抽象基类""" + + def __init__(self, equation): + self.equation = equation + + @abstractmethod + def exact_solution(self, x, t, initial_condition): + """ + 计算给定初始条件的解析解 + :param x: 空间坐标数组 + :param t: 时间 + :param initial_condition: 初始条件对象 + :return: 解析解数组 + """ + pass + + @property + def wave_speed(self): + """获取波速(委托给方程)""" + return self.equation.wave_speed() + + def flux(self, u): + """计算通量(委托给方程)""" + return self.equation.flux(u) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/physics/systems/factory.py b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/systems/factory.py new file mode 100644 index 00000000..6fcc754e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/systems/factory.py @@ -0,0 +1,28 @@ +# src/physics/systems/factory.py +""" +物理系统工厂 +""" + +from core.registry import BaseFactory + +class PhysicalSystemFactory: + """物理系统工厂""" + + @staticmethod + def create(system_type: str, **kwargs): + """ + 创建物理系统实例 + + Args: + system_type: 系统类型,如 'linear_advection' + **kwargs: 传递给方程的参数 + + Returns: + 物理系统实例 + """ + return BaseFactory.create_component('system', system_type, **kwargs) + + @staticmethod + def get_available_types(): + """获取所有可用的系统类型""" + return BaseFactory.get_available_components('system') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/physics/systems/linear_advection_system.py b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/systems/linear_advection_system.py new file mode 100644 index 00000000..0f3a4901 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/physics/systems/linear_advection_system.py @@ -0,0 +1,36 @@ +# src/physics/systems/linear_advection_system.py +""" +线性对流物理系统 +""" + +from .base import PhysicalSystem +from core.registry import register_component +import numpy as np + +@register_component('system', 'linear_advection') +class LinearAdvectionSystem(PhysicalSystem): + """线性对流系统""" + + def exact_solution(self, x, t, initial_condition): + """ + 计算线性对流的解析解 + :param x: 空间坐标数组 + :param t: 时间 + :param initial_condition: 初始条件对象 + :return: 解析解数组 + """ + if len(x) == 0: + return np.zeros((1, 0)) + + L = x[-1] - x[0] # 假设均匀周期网格 + c = self.equation.wave_speed() + x_shifted = (x - c * t + L) % L + + # 调用初始条件的 evaluate_at 方法 + u0 = initial_condition.evaluate_at(x_shifted) + + # 确保返回形状正确 + if u0.ndim == 1: + u0 = u0[np.newaxis, :] # (1, ncells) + + return u0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/plotter.py b/example/1d-linear-convection/weno3/python-v1/02e/src/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/problem.py b/example/1d-linear-convection/weno3/python-v1/02e/src/problem.py new file mode 100644 index 00000000..53719f26 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/problem.py @@ -0,0 +1,38 @@ +# problem.py +""" +问题定义模块:每个 Problem 子类代表一个完整测试用例 +包含初始条件、解析解(可选)、物理方程等 +""" + +from abc import ABC, abstractmethod +from initial_condition import InitialConditionFactory + + +class Problem(ABC): + """ + 抽象问题基类 + 每个具体问题(如线性对流、Sod 激波管)应继承此类 + """ + def __init__(self, config): + self.config = config + + @abstractmethod + def initial_condition(self): + """ + 返回 InitialCondition 实例 + """ + pass + + def exact_solution(self, cfd): + """ + 可选:返回解析解(数值数组) + 若无解析解,可抛出 NotImplementedError 或返回 None + """ + x = cfd.domain.mesh.xcc + raise NotImplementedError( + f"Problem '{self.__class__.__name__}' does not provide an exact solution." + ) + + @property + def name(self): + return self.__class__.__name__ \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/result.py b/example/1d-linear-convection/weno3/python-v1/02e/src/result.py new file mode 100644 index 00000000..5e572216 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/result.py @@ -0,0 +1,25 @@ +# result.py + +class ResultAssembler: + @staticmethod + def assemble(cfd: 'Cfd') -> dict: + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied].copy() + + # ✅ 从 problem 获取解析解(唯一正确路径) + try: + analytical = cfd.problem.exact_solution(cfd) + except NotImplementedError: + analytical = None # 或 np.full_like(u_numerical, np.nan) + + return { + "x": cfd.domain.mesh.xcc, + "numerical": u_numerical, + "analytical": analytical, + "config": { + "scheme": cfd.config.recon_scheme, + "order": cfd.config.spatial_order, + "rk_order": cfd.config.rk_order, + "final_time": cfd.config.final_time, + "problem": cfd.problem.name, + } + } \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/solution.py b/example/1d-linear-convection/weno3/python-v1/02e/src/solution.py new file mode 100644 index 00000000..90666c34 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/solution.py @@ -0,0 +1,32 @@ +# solution.py +import numpy as np + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02e/src/solver.py b/example/1d-linear-convection/weno3/python-v1/02e/src/solver.py new file mode 100644 index 00000000..fe222204 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02e/src/solver.py @@ -0,0 +1,68 @@ +# solver.py + +from mesh import Mesh +from domain import Domain +from solution import Solution +from config import CfdConfig +from result import ResultAssembler +from physics.problems.base import Problem +from physics.initial_conditions import InitialConditionFactory +from boundary.factory import BoundaryConditionFactory + +from numerics.time_integration.factory import TimeIntegratorFactory +from numerics.residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, problem: Problem, mesh): + self.problem = problem + self.config = problem.config + self.domain = Domain(problem.config, mesh) + self.solution = Solution(problem.config, self.domain) + + self.initial_condition = InitialConditionFactory.create(self.config) + self.boundary_condition = BoundaryConditionFactory.create(self) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + + # ==================== 公共接口(供 TimeIntegrator 调用) ==================== + def compute_residual(self): + """计算物理残差(封装重建→通量→散度)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件""" + self.boundary_condition.apply(self.solution.u) + + # ==================== 初始化 ==================== + def initialize(self): + """初始化全场:先 IC,再 BC,最后同步 old field""" + self.initial_condition.apply(self.solution) + # 应用边界条件到初始场 + self.apply_boundary() + # 同步 old field + self.solution.update_old_field() + + + def run(self): + self.initialize() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + self.result = ResultAssembler.assemble(self) + return self.result["numerical"] + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/example/run_eno_weno.py b/example/1d-linear-convection/weno3/python-v1/02f/example/run_eno_weno.py new file mode 100644 index 00000000..8f8bb8de --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/example/run_eno_weno.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +# example/run_eno_weno.py + +import sys +import os +print(f"当前工作目录: {os.getcwd()}") +print(f"Python路径:") +for path in sys.path: + print(f" {path}") + +src_path = os.path.join(os.path.dirname(__file__), '..', 'src') +print(f"\n尝试添加src路径: {src_path}") +print(f"src路径是否存在: {os.path.exists(src_path)}") +print(f"physics目录是否存在: {os.path.exists(os.path.join(src_path, 'physics'))}") +print(f"initial_conditions目录是否存在: {os.path.exists(os.path.join(src_path, 'physics', 'initial_conditions'))}") + +if src_path not in sys.path: + sys.path.insert(0, src_path) + +from solver import Cfd +from config import CfdConfig +from infrastructure.mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter +from physics.problems.linear_advection import LinearAdvectionProblem + + +def performEnoWenoAnalysisBAK(): + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + print("Running ENO3 solver...") + config_eno3 = CfdConfig() + config_eno3.with_reconstruction("eno", 3) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno3 = LinearAdvectionProblem(config_eno3) + cfd_eno3 = Cfd(problem_eno3, mesh) # ← 注入 problem + cfd_eno3.run() + + # 3. 配置并运行 WENO3 求解 + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno3 = LinearAdvectionProblem(config_weno3) + cfd_weno3 = Cfd(problem_weno3, mesh) # ← 注入 problem + cfd_weno3.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" + ) + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO5 求解 + print("Running ENO5 solver...") + config_eno5 = CfdConfig() + config_eno5.with_reconstruction("eno", 5) + config_eno5.dt = 0.0025 + config_eno5.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno5 = LinearAdvectionProblem(config_eno5) + cfd_eno5 = Cfd(problem_eno5, mesh) # ← 注入 problem + cfd_eno5.run() + + # 3. 配置并运行 WENO5求解 + print("Running WENO5 solver...") + config_weno5 = CfdConfig() + config_weno5.with_reconstruction("weno", 5) + config_weno5.dt = 0.0025 + config_weno5.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno5 = LinearAdvectionProblem(config_weno5) + cfd_weno5 = Cfd(problem_weno5, mesh) # ← 注入 problem + cfd_weno5.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno5.result, + weno_result=cfd_weno5.result, + save_path="eno_weno_comparison.png" + ) + + +if __name__ == "__main__": + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/config.py b/example/1d-linear-convection/weno3/python-v1/02f/src/config.py new file mode 100644 index 00000000..6432c80e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/config.py @@ -0,0 +1,45 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + #self.ic_type = "sin" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # "rusanov", "engquist-osher" + #self.flux_type = "engquist-osher" # "rusanov", "engquist-osher" + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + print(f"scheme={scheme}") + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/core/registry.py b/example/1d-linear-convection/weno3/python-v1/02f/src/core/registry.py new file mode 100644 index 00000000..4a52afc2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/core/registry.py @@ -0,0 +1,83 @@ +# core/registry.py +""" +统一注册系统 + 通用工厂 +- ComponentRegistry: 组件注册表 +- register_component: 装饰器 +- BaseFactory: 通用工厂类 +""" + +from typing import Dict, Type, Any + + +# ==================== 1. 注册表核心 ==================== +class ComponentRegistry: + """组件注册表""" + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True + + @classmethod + def set_verbose(cls, verbose: bool): + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + #print(f"ComponentRegistry register cls={cls}") + #print(f"ComponentRegistry register name={name}") + #print(f"ComponentRegistry register component_class={component_class}") + if category not in cls._registries: + cls._registries[category] = {} + if name in cls._registries[category]: + if cls._registries[category][name] != component_class and cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + if category not in cls._registries: + raise ValueError(f"❌ 未知类别: {category} (可用: {list(cls._registries.keys())})") + if name not in cls._registries[category]: + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {list(cls._registries[category].keys())})") + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) for cat, comps in cls._registries.items()} + + +# ==================== 2. 装饰器 ==================== +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + #print(f"register_component decorator name={name}") + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + +# ==================== 3. 通用工厂 ==================== +class BaseFactory: + """通用工厂基类""" + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + name_lower = name.lower() + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + available = ComponentRegistry.list_all().get(category, []) + if available: + error_msg = f"不支持的 {category} 类型 '{name}'。可用类型:{available}" + else: + error_msg = f"不支持的 {category} 类型 '{name}'(无已注册组件)" + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/__init__.py b/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/__init__.py new file mode 100644 index 00000000..e476afcb --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/__init__.py @@ -0,0 +1,18 @@ +# src/infrastructure/__init__.py +""" +基础设施模块 +包含网格、域、解、边界条件等基础组件 +""" + +from .mesh import Mesh +from .domain import Domain +from .solution import Solution +from .boundary import BoundaryCondition, BoundaryConditionFactory + +__all__ = [ + 'Mesh', + 'Domain', + 'Solution', + 'BoundaryCondition', + 'BoundaryConditionFactory', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/boundary/__init__.py b/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/boundary/__init__.py new file mode 100644 index 00000000..20120e8a --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/boundary/__init__.py @@ -0,0 +1,21 @@ +# src/boundary/__init__.py +""" +边界条件模块 +提供各种边界条件实现和工厂创建接口 +""" + +from .base import BoundaryCondition +from .factory import BoundaryConditionFactory + +# 导入具体类以触发注册 +from .periodic import PeriodicBoundary +from .dirichlet import DirichletBoundary +from .neumann import NeumannBoundary + +__all__ = [ + 'BoundaryCondition', + 'BoundaryConditionFactory', + 'PeriodicBoundary', + 'DirichletBoundary', + 'NeumannBoundary', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/boundary/base.py b/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/boundary/base.py new file mode 100644 index 00000000..cddf649d --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/boundary/base.py @@ -0,0 +1,18 @@ +# src/boundary/base.py +from abc import ABC, abstractmethod + +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/boundary/dirichlet.py b/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/boundary/dirichlet.py new file mode 100644 index 00000000..1eff3699 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/boundary/dirichlet.py @@ -0,0 +1,27 @@ +# src/boundary/dirichlet.py +from .base import BoundaryCondition +from core.registry import register_component + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/boundary/factory.py b/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/boundary/factory.py new file mode 100644 index 00000000..4bd7ae55 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/boundary/factory.py @@ -0,0 +1,24 @@ +# src/boundary/factory.py +from core.registry import BaseFactory + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + + @staticmethod + def get_available_types(): + """获取所有可用的边界条件类型""" + return BaseFactory.get_available_components('boundary') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/boundary/neumann.py b/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/boundary/neumann.py new file mode 100644 index 00000000..6eb72f64 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/boundary/neumann.py @@ -0,0 +1,23 @@ +# src/boundary/neumann.py +from .base import BoundaryCondition +from core.registry import register_component + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/boundary/periodic.py b/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/boundary/periodic.py new file mode 100644 index 00000000..bd601d95 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/boundary/periodic.py @@ -0,0 +1,19 @@ +# src/boundary/periodic.py +from .base import BoundaryCondition +from core.registry import register_component + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/domain.py b/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/domain.py new file mode 100644 index 00000000..05402f4c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/domain.py @@ -0,0 +1,56 @@ +# src/infrastructure/domain.py +from .mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/mesh.py b/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/solution.py b/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/solution.py new file mode 100644 index 00000000..c3a00dd5 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/infrastructure/solution.py @@ -0,0 +1,32 @@ +# src/infrastructure/solution.py +import numpy as np + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/__init__.py b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/__init__.py new file mode 100644 index 00000000..4cfdcc73 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/__init__.py @@ -0,0 +1,27 @@ +# src/numerics/__init__.py +""" +数值方法模块 +包含重构器、通量计算、时间积分等数值方法 +""" + +from .residual import ResidualCalculator +from .reconstructor import Reconstructor, ReconstructorFactory +from .flux import InviscidFluxCalculator, FluxCalculatorFactory +from .time_integration import TimeIntegrator, TimeIntegratorFactory + +__all__ = [ + # 残差计算 + 'ResidualCalculator', + + # 重构器 + 'Reconstructor', + 'ReconstructorFactory', + + # 通量计算 + 'InviscidFluxCalculator', + 'FluxCalculatorFactory', + + # 时间积分 + 'TimeIntegrator', + 'TimeIntegratorFactory', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/flux/__init__.py b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/flux/__init__.py new file mode 100644 index 00000000..c39d6253 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/flux/__init__.py @@ -0,0 +1,19 @@ +# src/numerics/flux/__init__.py +""" +通量计算模块 +提供Rusanov、Engquist-Osher等通量计算方法 +""" + +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 导入具体实现以触发注册 +from .rusanov import RusanovFluxCalculator +from .engquist_osher import EngquistOsherFluxCalculator + +__all__ = [ + 'InviscidFluxCalculator', + 'FluxCalculatorFactory', + 'RusanovFluxCalculator', + 'EngquistOsherFluxCalculator', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/flux/base.py b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/flux/engquist_osher.py new file mode 100644 index 00000000..16dbce82 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/flux/engquist_osher.py @@ -0,0 +1,20 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + eq = self.cfd.problem.physical_system().equation + for i in range(self.mesh.nnodes): + c = eq.wave_speed() + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/flux/factory.py b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/flux/factory.py new file mode 100644 index 00000000..c35f6e96 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/flux/factory.py @@ -0,0 +1,26 @@ +# src/numerics/flux/factory.py +from core.registry import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + + @staticmethod + def get_available_types(): + """获取所有可用的重构器类型""" + from core.registry import BaseFactory + return BaseFactory.get_available_components('flux') + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/flux/rusanov.py b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/flux/rusanov.py new file mode 100644 index 00000000..56daa699 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/flux/rusanov.py @@ -0,0 +1,25 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量,使用 Equation 解耦物理参数""" + + def compute(self, q_face_left, q_face_right, flux): + # 从 cfd 获取 equation(与 Julia 对齐) + eq = self.cfd.problem.physical_system().equation + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = eq.wave_speed() + c_R = eq.wave_speed() + # 通过 equation 计算通量和波速 + F_L = eq.flux(u_L) + F_R = eq.flux(u_R) + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/reconstructor/__init__.py new file mode 100644 index 00000000..6b0a3d9b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/reconstructor/__init__.py @@ -0,0 +1,21 @@ +# src/numerics/reconstructor/__init__.py +""" +数值重构模块 +提供ENO、WENO等界面值重构方法 +""" + +from .base import Reconstructor +from .factory import ReconstructorFactory + +# 导入具体实现以触发注册 +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from .weno5 import Weno5Reconstructor + +__all__ = [ + 'Reconstructor', + 'ReconstructorFactory', + 'EnoReconstructor', + 'Weno3Reconstructor', + 'Weno5Reconstructor', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/reconstructor/base.py b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/reconstructor/base.py new file mode 100644 index 00000000..de00327c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def compute_face_values(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/reconstructor/eno.py b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/reconstructor/eno.py new file mode 100644 index 00000000..60699adf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/reconstructor/eno.py @@ -0,0 +1,96 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.initialize(self.config.spatial_order, self.domain.ntcells) + + def initialize(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def compute_face_values(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/reconstructor/factory.py b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/reconstructor/factory.py new file mode 100644 index 00000000..1f297eaa --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/reconstructor/factory.py @@ -0,0 +1,20 @@ +# src/numerics/reconstructor/factory.py +from core.registry import BaseFactory + +class ReconstructorFactory: + @staticmethod + def create(cfd): + config = cfd.config + scheme = config.recon_scheme.lower() + + if scheme.startswith("weno"): + if scheme == "weno": + order = getattr(config, 'spatial_order', None) + scheme = f"weno{order}" + return BaseFactory.create_component('reconstructor', scheme, cfd) + + @staticmethod + def get_available_types(): + """获取所有可用的重构器类型""" + from core.registry import BaseFactory + return BaseFactory.get_available_components('reconstructor') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/reconstructor/weno3.py new file mode 100644 index 00000000..f45eb35f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/reconstructor/weno3.py @@ -0,0 +1,62 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def __init__(self, cfd): + self.cfd = cfd + + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/reconstructor/weno5.py b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/reconstructor/weno5.py new file mode 100644 index 00000000..9d665275 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/reconstructor/weno5.py @@ -0,0 +1,71 @@ +# reconstructor/weno5.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno5') +class Weno5Reconstructor(Reconstructor): + def __init__(self, cfd): + self.cfd = cfd + + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3, v4, v5) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3, v4, v5) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 1.0/10.0 + d1 = 3.0/5.0 + d2 = 3.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = 1.0/3.0*v1-7.0/6.0*v2+11.0/6.0*v3 # r=2 + q1 = -1.0/6.0*v2+5.0/6.0*v3+1.0/3.0*v4 # r=1 + q2 = 1.0/3.0*v3+5.0/6.0*v4-1.0/6.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 3.0/10.0 + d1 = 3.0/5.0 + d2 = 1.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = -1.0/6.0*v1+5.0/6.0*v2+1.0/3.0*v3 # r=2 + q1 = 1.0/3.0*v2+5.0/6.0*v3-1.0/6.0*v4 # r=1 + q2 = 11.0/6.0*v3-7.0/6.0*v4+1.0/3.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/residual.py b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/residual.py new file mode 100644 index 00000000..2f96f05b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/residual.py @@ -0,0 +1,39 @@ +# src/numerics/residual.py +from numerics.flux.factory import FluxCalculatorFactory +from numerics.reconstructor.factory import ReconstructorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + + self.reconstructor = ReconstructorFactory.create(cfd) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._compute_face_values() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _compute_face_values(self): + """私有方法:界面值重建""" + self.reconstructor.compute_face_values(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/time_integration/__init__.py b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/time_integration/__init__.py new file mode 100644 index 00000000..9c127c77 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/time_integration/__init__.py @@ -0,0 +1,21 @@ +# src/numerics/time_integration/__init__.py +""" +时间积分模块 +提供显式Runge-Kutta方法 +""" + +from .base import TimeIntegrator +from .factory import TimeIntegratorFactory + +# 导入具体实现以触发注册 +from .rk1 import RK1Integrator +from .rk2 import RK2Integrator +from .rk3 import RK3Integrator + +__all__ = [ + 'TimeIntegrator', + 'TimeIntegratorFactory', + 'RK1Integrator', + 'RK2Integrator', + 'RK3Integrator', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/time_integration/base.py b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/time_integration/base.py new file mode 100644 index 00000000..0cdf29a4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/time_integration/base.py @@ -0,0 +1,27 @@ +# time_integration/base.py + +from abc import ABC, abstractmethod + +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + + @abstractmethod + def step(self, dt): + pass + + def compute_residual(self): + """计算残差(委托给 Cfd)""" + self.cfd.compute_residual() + + def apply_boundary(self): + """应用边界条件(委托给 Cfd)""" + self.cfd.apply_boundary() + + def map_idx(self, i): + """物理网格索引 → 残差数组索引""" + return i - self.domain.ist \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/time_integration/factory.py b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/time_integration/factory.py new file mode 100644 index 00000000..5e6f9779 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/time_integration/factory.py @@ -0,0 +1,15 @@ +# src/numerics/time_integration/factory.py +from core.registry import BaseFactory + +class TimeIntegratorFactory: + @staticmethod + def create(cfd) -> 'TimeIntegrator': + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) + + @staticmethod + def get_available_types(): + """获取所有可用的重构器类型""" + from core.registry import BaseFactory + return BaseFactory.get_available_components('integrator') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/time_integration/rk1.py b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/time_integration/rk1.py new file mode 100644 index 00000000..b4c8a021 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/time_integration/rk1.py @@ -0,0 +1,15 @@ +# time_integration/rk1.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/time_integration/rk2.py b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/time_integration/rk2.py new file mode 100644 index 00000000..6d2be304 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/time_integration/rk2.py @@ -0,0 +1,29 @@ +# time_integration/rk2.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + 0.5 * self.solution.un[i] + + 0.5 * self.solution.u[i] + + 0.5 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/time_integration/rk3.py b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/time_integration/rk3.py new file mode 100644 index 00000000..c70791e1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/numerics/time_integration/rk3.py @@ -0,0 +1,43 @@ +# time_integration/rk3.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # Stage 1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # Stage 2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = ( + 0.75 * self.solution.un[i] + + 0.25 * self.solution.u[i] + + 0.25 * dt * self.solution.res[j] + ) + self.solution.u[:] = u2 + self.apply_boundary() + + # Stage 3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + c1 * self.solution.un[i] + + c2 * self.solution.u[i] + + c3 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/physics/__init__.py b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/physics/equations/__init__.py b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/equations/__init__.py new file mode 100644 index 00000000..14b82117 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/equations/__init__.py @@ -0,0 +1,15 @@ +# src/physics/equations/__init__.py +""" +物理方程模块 +提供各种控制方程的实现 +""" + +from .base import Equation +from .linear_advection import LinearAdvectionEquation +from .euler import EulerEquation + +__all__ = [ + 'Equation', + 'LinearAdvectionEquation', + 'EulerEquation', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/physics/equations/base.py b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/equations/base.py new file mode 100644 index 00000000..05833e74 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/equations/base.py @@ -0,0 +1,60 @@ +# src/physics/equations/base.py +""" +物理方程抽象基类 +所有控制方程必须继承此类 +""" + +from abc import ABC, abstractmethod +import numpy as np + +class Equation(ABC): + """ + 控制方程抽象基类 + 所有物理方程(线性对流、Euler、MHD)必须继承此类 + """ + + @property + @abstractmethod + def num_equations(self) -> int: + """返回方程组变量数(标量=1,Euler=3,MHD=8)""" + pass + + @abstractmethod + def flux(self, u): + """ + 计算通量函数 f(u) + :param u: 状态向量 (num_equations,) + :return: 通量向量 (num_equations,) + """ + pass + + @abstractmethod + def max_wave_speed(self, u): + """ + 计算最大波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + @abstractmethod + def wave_speed(self): + """ + 计算波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + def exact_solution(self, x, t, initial_condition_func): + """ + 可选:提供解析解 + 子类可重写;若无解析解,调用方应捕获 NotImplementedError + :param x: 空间坐标数组 (ncells,) + :param t: 时间 + :param initial_condition_func: 初值函数 u0(x) -> (num_equations, ncells) + :return: 解 u(x,t) -> (num_equations, ncells) + """ + raise NotImplementedError( + f"Equation '{self.__class__.__name__}' does not provide an exact solution." + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/physics/equations/euler.py b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/equations/euler.py new file mode 100644 index 00000000..0b69c2d7 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/equations/euler.py @@ -0,0 +1,23 @@ +# src/physics/equations/euler.py +""" +欧拉方程(占位文件,未来实现) +""" +from .base import Equation + +class EulerEquation(Equation): + """欧拉方程:质量、动量、能量守恒""" + def __init__(self, gamma=1.4): + self.gamma = gamma + + @property + def num_equations(self) -> int: + return 3 + + def flux(self, u): + raise NotImplementedError("欧拉方程待实现") + + def max_wave_speed(self, u): + raise NotImplementedError("欧拉方程待实现") + + def wave_speed(self): + raise NotImplementedError("欧拉方程待实现") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/physics/equations/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/equations/linear_advection.py new file mode 100644 index 00000000..949c4a0a --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/equations/linear_advection.py @@ -0,0 +1,42 @@ +# src/physics/equations/linear_advection.py +""" +线性对流方程: u_t + c * u_x = 0 +支持标量(num_equations=1) +""" + +import numpy as np +from .base import Equation +from core.registry import register_component + +@register_component('equation', 'linear_advection') +class LinearAdvectionEquation(Equation): + def __init__(self, wave_speed: float): + self.c = wave_speed + + @property + def num_equations(self) -> int: + return 1 + + def flux(self, u): + """f(u) = c * u""" + return self.c * u + + def max_wave_speed(self, u): + """最大波速 = |c|""" + return abs(self.c) + + def wave_speed(self): + return self.c + + def exact_solution(self, x, t, initial_condition_func): + """ + 解析解: u(x, t) = u0(x - c * t) + 支持周期边界 + """ + if len(x) == 0: + return np.zeros((1, 0)) + + L = x[-1] - x[0] # 假设均匀周期网格 + x_shifted = (x - self.c * t + L) % L + u0 = initial_condition_func(x_shifted) # (1, ncells) + return u0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/physics/initial_conditions/__init__.py b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/initial_conditions/__init__.py new file mode 100644 index 00000000..94b0ad80 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/initial_conditions/__init__.py @@ -0,0 +1,16 @@ +# src/physics/initial_conditions/__init__.py +from .base import InitialCondition +from .factory import InitialConditionFactory + +# 导入具体类以触发注册 +from .step import StepFunctionIC +from .sine import SineWaveIC +from .gaussian import GaussianPulseIC + +__all__ = [ + 'InitialCondition', + 'InitialConditionFactory', + 'StepFunctionIC', + 'SineWaveIC', + 'GaussianPulseIC', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/physics/initial_conditions/base.py b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/initial_conditions/base.py new file mode 100644 index 00000000..8a33a0d5 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/initial_conditions/base.py @@ -0,0 +1,25 @@ +# src/initial_conditions/base.py +from abc import ABC, abstractmethod +import numpy as np + +class InitialCondition(ABC): + """初始条件抽象基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + """内部辅助方法:将值应用到物理区域""" + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/physics/initial_conditions/factory.py b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/initial_conditions/factory.py new file mode 100644 index 00000000..2a7558b2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/initial_conditions/factory.py @@ -0,0 +1,24 @@ +# src/initial_conditions/factory.py +from core.registry import BaseFactory +from .base import InitialCondition + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + config: 配置对象,包含ic_type属性 + + Returns: + 初始条件实例 + """ + return BaseFactory.create_component('initial_condition', config.ic_type, config) + + @classmethod + def get_available_types(cls): + """获取所有可用的初始条件类型""" + return BaseFactory.get_available_components('initial_condition') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/physics/initial_conditions/gaussian.py b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/initial_conditions/gaussian.py new file mode 100644 index 00000000..1dad463b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/initial_conditions/gaussian.py @@ -0,0 +1,16 @@ +# src/initial_conditions/gaussian.py +import numpy as np +from .base import InitialCondition +from core.registry import register_component + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/physics/initial_conditions/sine.py b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/initial_conditions/sine.py new file mode 100644 index 00000000..c061fa5a --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/initial_conditions/sine.py @@ -0,0 +1,15 @@ +# src/initial_conditions/sine.py +import numpy as np +from .base import InitialCondition +from core.registry import register_component + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/physics/initial_conditions/step.py b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/initial_conditions/step.py new file mode 100644 index 00000000..ff1120b2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/initial_conditions/step.py @@ -0,0 +1,17 @@ +# src/initial_conditions/step.py +import numpy as np +from .base import InitialCondition +from core.registry import register_component + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/physics/problems/__init__.py b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/problems/__init__.py new file mode 100644 index 00000000..e9d32905 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/problems/__init__.py @@ -0,0 +1,14 @@ +# src/physics/problems/__init__.py +""" +问题定义模块 +""" + +from .base import Problem +from .factory import ProblemFactory +from .linear_advection import LinearAdvectionProblem + +__all__ = [ + 'Problem', + 'ProblemFactory', + 'LinearAdvectionProblem', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/physics/problems/base.py b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/problems/base.py new file mode 100644 index 00000000..ddd8ddc4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/problems/base.py @@ -0,0 +1,42 @@ +# src/physics/problems/base.py +""" +问题定义抽象基类 +每个具体问题(如线性对流、Sod 激波管)应继承此类 +""" + +from abc import ABC, abstractmethod + +class Problem(ABC): + """ + 抽象问题基类 + """ + def __init__(self, config): + self.config = config + self._initial_condition = None + self._physical_system = None + + @abstractmethod + def initial_condition(self): + """返回 InitialCondition 实例""" + pass + + @abstractmethod + def physical_system(self): + """返回 PhysicalSystem 实例""" + pass + + def exact_solution(self, cfd): + """ + 计算解析解(委托给物理系统) + """ + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + ic = self.initial_condition() + system = self.physical_system() + + u_exact = system.exact_solution(x, t, ic) + return u_exact[0] # 返回标量数组 (ncells,) 以兼容现有绘图逻辑 + + @property + def name(self): + return self.__class__.__name__ \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/physics/problems/factory.py b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/problems/factory.py new file mode 100644 index 00000000..b3e3563f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/problems/factory.py @@ -0,0 +1,28 @@ +# src/physics/problems/factory.py +""" +问题工厂 +""" + +from core.registry import BaseFactory + +class ProblemFactory: + """问题工厂""" + + @staticmethod + def create(problem_type: str, config): + """ + 创建问题实例 + + Args: + problem_type: 问题类型,如 'linear_advection' + config: 配置对象 + + Returns: + 问题实例 + """ + return BaseFactory.create_component('problem', problem_type, config) + + @staticmethod + def get_available_types(): + """获取所有可用的问题类型""" + return BaseFactory.get_available_components('problem') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/physics/problems/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/problems/linear_advection.py new file mode 100644 index 00000000..f19ea53a --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/problems/linear_advection.py @@ -0,0 +1,36 @@ +# src/physics/problems/linear_advection.py +""" +线性对流问题 +""" + +from .base import Problem +from physics.equations.linear_advection import LinearAdvectionEquation +from physics.systems.linear_advection_system import LinearAdvectionSystem +from physics.initial_conditions.factory import InitialConditionFactory +from core.registry import register_component + +@register_component('problem', 'linear_advection') +class LinearAdvectionProblem(Problem): + """ + 线性对流问题:u_t + c u_x = 0 + 使用周期边界 + 任意初始条件 + """ + + def __init__(self, config): + super().__init__(config) + + def initial_condition(self): + """返回初始条件实例""" + if self._initial_condition is None: + from physics.initial_conditions.factory import InitialConditionFactory + self._initial_condition = InitialConditionFactory.create(self.config) + return self._initial_condition + + def physical_system(self): + """返回物理系统实例""" + if self._physical_system is None: + # 创建方程 + equation = LinearAdvectionEquation(wave_speed=self.config.wave_speed) + # 创建系统 + self._physical_system = LinearAdvectionSystem(equation) + return self._physical_system \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/physics/systems/__init__.py b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/systems/__init__.py new file mode 100644 index 00000000..bd22409c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/systems/__init__.py @@ -0,0 +1,15 @@ +# src/physics/systems/__init__.py +""" +物理系统模块 +组合方程和解析解逻辑 +""" + +from .base import PhysicalSystem +from .factory import PhysicalSystemFactory +from .linear_advection_system import LinearAdvectionSystem + +__all__ = [ + 'PhysicalSystem', + 'PhysicalSystemFactory', + 'LinearAdvectionSystem', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/physics/systems/base.py b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/systems/base.py new file mode 100644 index 00000000..957212d0 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/systems/base.py @@ -0,0 +1,33 @@ +# src/physics/systems/base.py +""" +物理系统基类 +将方程与解析解等逻辑组合 +""" + +from abc import ABC, abstractmethod + +class PhysicalSystem(ABC): + """物理系统抽象基类""" + + def __init__(self, equation): + self.equation = equation + + @abstractmethod + def exact_solution(self, x, t, initial_condition): + """ + 计算给定初始条件的解析解 + :param x: 空间坐标数组 + :param t: 时间 + :param initial_condition: 初始条件对象 + :return: 解析解数组 + """ + pass + + @property + def wave_speed(self): + """获取波速(委托给方程)""" + return self.equation.wave_speed() + + def flux(self, u): + """计算通量(委托给方程)""" + return self.equation.flux(u) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/physics/systems/factory.py b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/systems/factory.py new file mode 100644 index 00000000..6fcc754e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/systems/factory.py @@ -0,0 +1,28 @@ +# src/physics/systems/factory.py +""" +物理系统工厂 +""" + +from core.registry import BaseFactory + +class PhysicalSystemFactory: + """物理系统工厂""" + + @staticmethod + def create(system_type: str, **kwargs): + """ + 创建物理系统实例 + + Args: + system_type: 系统类型,如 'linear_advection' + **kwargs: 传递给方程的参数 + + Returns: + 物理系统实例 + """ + return BaseFactory.create_component('system', system_type, **kwargs) + + @staticmethod + def get_available_types(): + """获取所有可用的系统类型""" + return BaseFactory.get_available_components('system') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/physics/systems/linear_advection_system.py b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/systems/linear_advection_system.py new file mode 100644 index 00000000..0f3a4901 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/physics/systems/linear_advection_system.py @@ -0,0 +1,36 @@ +# src/physics/systems/linear_advection_system.py +""" +线性对流物理系统 +""" + +from .base import PhysicalSystem +from core.registry import register_component +import numpy as np + +@register_component('system', 'linear_advection') +class LinearAdvectionSystem(PhysicalSystem): + """线性对流系统""" + + def exact_solution(self, x, t, initial_condition): + """ + 计算线性对流的解析解 + :param x: 空间坐标数组 + :param t: 时间 + :param initial_condition: 初始条件对象 + :return: 解析解数组 + """ + if len(x) == 0: + return np.zeros((1, 0)) + + L = x[-1] - x[0] # 假设均匀周期网格 + c = self.equation.wave_speed() + x_shifted = (x - c * t + L) % L + + # 调用初始条件的 evaluate_at 方法 + u0 = initial_condition.evaluate_at(x_shifted) + + # 确保返回形状正确 + if u0.ndim == 1: + u0 = u0[np.newaxis, :] # (1, ncells) + + return u0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/plotter.py b/example/1d-linear-convection/weno3/python-v1/02f/src/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/result.py b/example/1d-linear-convection/weno3/python-v1/02f/src/result.py new file mode 100644 index 00000000..5e572216 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/result.py @@ -0,0 +1,25 @@ +# result.py + +class ResultAssembler: + @staticmethod + def assemble(cfd: 'Cfd') -> dict: + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied].copy() + + # ✅ 从 problem 获取解析解(唯一正确路径) + try: + analytical = cfd.problem.exact_solution(cfd) + except NotImplementedError: + analytical = None # 或 np.full_like(u_numerical, np.nan) + + return { + "x": cfd.domain.mesh.xcc, + "numerical": u_numerical, + "analytical": analytical, + "config": { + "scheme": cfd.config.recon_scheme, + "order": cfd.config.spatial_order, + "rk_order": cfd.config.rk_order, + "final_time": cfd.config.final_time, + "problem": cfd.problem.name, + } + } \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02f/src/solver.py b/example/1d-linear-convection/weno3/python-v1/02f/src/solver.py new file mode 100644 index 00000000..7bbd4349 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02f/src/solver.py @@ -0,0 +1,74 @@ +# solver.py + +#from mesh import Mesh +#from domain import Domain +#from solution import Solution +from config import CfdConfig +from result import ResultAssembler +from physics.problems.base import Problem +from physics.initial_conditions import InitialConditionFactory +#from boundary.factory import BoundaryConditionFactory + +from infrastructure.boundary.factory import BoundaryConditionFactory +from infrastructure.mesh import Mesh +from infrastructure.domain import Domain +from infrastructure.solution import Solution + + +from numerics.time_integration.factory import TimeIntegratorFactory +from numerics.residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, problem: Problem, mesh): + self.problem = problem + self.config = problem.config + self.domain = Domain(problem.config, mesh) + self.solution = Solution(problem.config, self.domain) + + self.initial_condition = InitialConditionFactory.create(self.config) + self.boundary_condition = BoundaryConditionFactory.create(self) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + + # ==================== 公共接口(供 TimeIntegrator 调用) ==================== + def compute_residual(self): + """计算物理残差(封装重建→通量→散度)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件""" + self.boundary_condition.apply(self.solution.u) + + # ==================== 初始化 ==================== + def initialize(self): + """初始化全场:先 IC,再 BC,最后同步 old field""" + self.initial_condition.apply(self.solution) + # 应用边界条件到初始场 + self.apply_boundary() + # 同步 old field + self.solution.update_old_field() + + + def run(self): + self.initialize() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + self.result = ResultAssembler.assemble(self) + return self.result["numerical"] + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/example/run_eno_weno.py b/example/1d-linear-convection/weno3/python-v1/02g/example/run_eno_weno.py new file mode 100644 index 00000000..d210c6f4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/example/run_eno_weno.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +# example/run_eno_weno.py + +import sys +import os + +src_path = os.path.join(os.path.dirname(__file__), '..', 'src') +if src_path not in sys.path: + sys.path.insert(0, src_path) + +from core.solver import Cfd +from infrastructure.config import CfdConfig +from infrastructure.mesh import Mesh +from visualization.plotter import plot_eno_weno_comparison, CFDPlotter +from physics.problems.linear_advection import LinearAdvectionProblem + +def performEnoWenoAnalysisBAK(): + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + print("Running ENO3 solver...") + config_eno3 = CfdConfig() + config_eno3.with_reconstruction("eno", 3) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno3 = LinearAdvectionProblem(config_eno3) + cfd_eno3 = Cfd(problem_eno3, mesh) # ← 注入 problem + cfd_eno3.run() + + # 3. 配置并运行 WENO3 求解 + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno3 = LinearAdvectionProblem(config_weno3) + cfd_weno3 = Cfd(problem_weno3, mesh) # ← 注入 problem + cfd_weno3.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" + ) + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO5 求解 + print("Running ENO5 solver...") + config_eno5 = CfdConfig() + config_eno5.with_reconstruction("eno", 5) + config_eno5.dt = 0.0025 + config_eno5.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno5 = LinearAdvectionProblem(config_eno5) + cfd_eno5 = Cfd(problem_eno5, mesh) # ← 注入 problem + cfd_eno5.run() + + # 3. 配置并运行 WENO5求解 + print("Running WENO5 solver...") + config_weno5 = CfdConfig() + config_weno5.with_reconstruction("weno", 5) + config_weno5.dt = 0.0025 + config_weno5.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno5 = LinearAdvectionProblem(config_weno5) + cfd_weno5 = Cfd(problem_weno5, mesh) # ← 注入 problem + cfd_weno5.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno5.result, + weno_result=cfd_weno5.result, + save_path="eno_weno_comparison.png" + ) + + +if __name__ == "__main__": + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/core/registry.py b/example/1d-linear-convection/weno3/python-v1/02g/src/core/registry.py new file mode 100644 index 00000000..dad44ec7 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/core/registry.py @@ -0,0 +1,83 @@ +# src/core/registry.py +""" +统一注册系统 + 通用工厂 +- ComponentRegistry: 组件注册表 +- register_component: 装饰器 +- BaseFactory: 通用工厂类 +""" + +from typing import Dict, Type, Any + + +# ==================== 1. 注册表核心 ==================== +class ComponentRegistry: + """组件注册表""" + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True + + @classmethod + def set_verbose(cls, verbose: bool): + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + #print(f"ComponentRegistry register cls={cls}") + #print(f"ComponentRegistry register name={name}") + #print(f"ComponentRegistry register component_class={component_class}") + if category not in cls._registries: + cls._registries[category] = {} + if name in cls._registries[category]: + if cls._registries[category][name] != component_class and cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + if category not in cls._registries: + raise ValueError(f"❌ 未知类别: {category} (可用: {list(cls._registries.keys())})") + if name not in cls._registries[category]: + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {list(cls._registries[category].keys())})") + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) for cat, comps in cls._registries.items()} + + +# ==================== 2. 装饰器 ==================== +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + #print(f"register_component decorator name={name}") + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + +# ==================== 3. 通用工厂 ==================== +class BaseFactory: + """通用工厂基类""" + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + name_lower = name.lower() + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + available = ComponentRegistry.list_all().get(category, []) + if available: + error_msg = f"不支持的 {category} 类型 '{name}'。可用类型:{available}" + else: + error_msg = f"不支持的 {category} 类型 '{name}'(无已注册组件)" + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/core/solver.py b/example/1d-linear-convection/weno3/python-v1/02g/src/core/solver.py new file mode 100644 index 00000000..d41de3ee --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/core/solver.py @@ -0,0 +1,67 @@ +# src/core/solver.py + +from infrastructure.config import CfdConfig +from infrastructure.result import ResultAssembler +from infrastructure.boundary.factory import BoundaryConditionFactory +from infrastructure.mesh import Mesh +from infrastructure.domain import Domain +from infrastructure.solution import Solution +from physics.problems.base import Problem +from physics.initial_conditions import InitialConditionFactory +from numerics.time_integration.factory import TimeIntegratorFactory +from numerics.residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, problem: Problem, mesh): + self.problem = problem + self.config = problem.config + self.domain = Domain(problem.config, mesh) + self.solution = Solution(problem.config, self.domain) + + self.initial_condition = InitialConditionFactory.create(self.config) + self.boundary_condition = BoundaryConditionFactory.create(self) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + + # ==================== 公共接口(供 TimeIntegrator 调用) ==================== + def compute_residual(self): + """计算物理残差(封装重建→通量→散度)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件""" + self.boundary_condition.apply(self.solution.u) + + # ==================== 初始化 ==================== + def initialize(self): + """初始化全场:先 IC,再 BC,最后同步 old field""" + self.initial_condition.apply(self.solution) + # 应用边界条件到初始场 + self.apply_boundary() + # 同步 old field + self.solution.update_old_field() + + + def run(self): + self.initialize() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + self.result = ResultAssembler.assemble(self) + return self.result["numerical"] + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/__init__.py b/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/__init__.py new file mode 100644 index 00000000..e476afcb --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/__init__.py @@ -0,0 +1,18 @@ +# src/infrastructure/__init__.py +""" +基础设施模块 +包含网格、域、解、边界条件等基础组件 +""" + +from .mesh import Mesh +from .domain import Domain +from .solution import Solution +from .boundary import BoundaryCondition, BoundaryConditionFactory + +__all__ = [ + 'Mesh', + 'Domain', + 'Solution', + 'BoundaryCondition', + 'BoundaryConditionFactory', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/boundary/__init__.py b/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/boundary/__init__.py new file mode 100644 index 00000000..20120e8a --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/boundary/__init__.py @@ -0,0 +1,21 @@ +# src/boundary/__init__.py +""" +边界条件模块 +提供各种边界条件实现和工厂创建接口 +""" + +from .base import BoundaryCondition +from .factory import BoundaryConditionFactory + +# 导入具体类以触发注册 +from .periodic import PeriodicBoundary +from .dirichlet import DirichletBoundary +from .neumann import NeumannBoundary + +__all__ = [ + 'BoundaryCondition', + 'BoundaryConditionFactory', + 'PeriodicBoundary', + 'DirichletBoundary', + 'NeumannBoundary', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/boundary/base.py b/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/boundary/base.py new file mode 100644 index 00000000..cddf649d --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/boundary/base.py @@ -0,0 +1,18 @@ +# src/boundary/base.py +from abc import ABC, abstractmethod + +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/boundary/dirichlet.py b/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/boundary/dirichlet.py new file mode 100644 index 00000000..1eff3699 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/boundary/dirichlet.py @@ -0,0 +1,27 @@ +# src/boundary/dirichlet.py +from .base import BoundaryCondition +from core.registry import register_component + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/boundary/factory.py b/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/boundary/factory.py new file mode 100644 index 00000000..4bd7ae55 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/boundary/factory.py @@ -0,0 +1,24 @@ +# src/boundary/factory.py +from core.registry import BaseFactory + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + + @staticmethod + def get_available_types(): + """获取所有可用的边界条件类型""" + return BaseFactory.get_available_components('boundary') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/boundary/neumann.py b/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/boundary/neumann.py new file mode 100644 index 00000000..6eb72f64 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/boundary/neumann.py @@ -0,0 +1,23 @@ +# src/boundary/neumann.py +from .base import BoundaryCondition +from core.registry import register_component + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/boundary/periodic.py b/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/boundary/periodic.py new file mode 100644 index 00000000..bd601d95 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/boundary/periodic.py @@ -0,0 +1,19 @@ +# src/boundary/periodic.py +from .base import BoundaryCondition +from core.registry import register_component + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/config.py b/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/config.py new file mode 100644 index 00000000..58a952eb --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/config.py @@ -0,0 +1,45 @@ +# src/infrastructure/config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + #self.ic_type = "sin" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # "rusanov", "engquist-osher" + #self.flux_type = "engquist-osher" # "rusanov", "engquist-osher" + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + print(f"scheme={scheme}") + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/domain.py b/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/domain.py new file mode 100644 index 00000000..05402f4c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/domain.py @@ -0,0 +1,56 @@ +# src/infrastructure/domain.py +from .mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/mesh.py b/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/result.py b/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/result.py new file mode 100644 index 00000000..6d70839b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/result.py @@ -0,0 +1,25 @@ +# src/infrastructure/result.py + +class ResultAssembler: + @staticmethod + def assemble(cfd: 'Cfd') -> dict: + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied].copy() + + # ✅ 从 problem 获取解析解(唯一正确路径) + try: + analytical = cfd.problem.exact_solution(cfd) + except NotImplementedError: + analytical = None # 或 np.full_like(u_numerical, np.nan) + + return { + "x": cfd.domain.mesh.xcc, + "numerical": u_numerical, + "analytical": analytical, + "config": { + "scheme": cfd.config.recon_scheme, + "order": cfd.config.spatial_order, + "rk_order": cfd.config.rk_order, + "final_time": cfd.config.final_time, + "problem": cfd.problem.name, + } + } \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/solution.py b/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/solution.py new file mode 100644 index 00000000..c3a00dd5 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/infrastructure/solution.py @@ -0,0 +1,32 @@ +# src/infrastructure/solution.py +import numpy as np + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/__init__.py b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/__init__.py new file mode 100644 index 00000000..4cfdcc73 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/__init__.py @@ -0,0 +1,27 @@ +# src/numerics/__init__.py +""" +数值方法模块 +包含重构器、通量计算、时间积分等数值方法 +""" + +from .residual import ResidualCalculator +from .reconstructor import Reconstructor, ReconstructorFactory +from .flux import InviscidFluxCalculator, FluxCalculatorFactory +from .time_integration import TimeIntegrator, TimeIntegratorFactory + +__all__ = [ + # 残差计算 + 'ResidualCalculator', + + # 重构器 + 'Reconstructor', + 'ReconstructorFactory', + + # 通量计算 + 'InviscidFluxCalculator', + 'FluxCalculatorFactory', + + # 时间积分 + 'TimeIntegrator', + 'TimeIntegratorFactory', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/flux/__init__.py b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/flux/__init__.py new file mode 100644 index 00000000..c39d6253 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/flux/__init__.py @@ -0,0 +1,19 @@ +# src/numerics/flux/__init__.py +""" +通量计算模块 +提供Rusanov、Engquist-Osher等通量计算方法 +""" + +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 导入具体实现以触发注册 +from .rusanov import RusanovFluxCalculator +from .engquist_osher import EngquistOsherFluxCalculator + +__all__ = [ + 'InviscidFluxCalculator', + 'FluxCalculatorFactory', + 'RusanovFluxCalculator', + 'EngquistOsherFluxCalculator', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/flux/base.py b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/flux/engquist_osher.py new file mode 100644 index 00000000..16dbce82 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/flux/engquist_osher.py @@ -0,0 +1,20 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + eq = self.cfd.problem.physical_system().equation + for i in range(self.mesh.nnodes): + c = eq.wave_speed() + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/flux/factory.py b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/flux/factory.py new file mode 100644 index 00000000..c35f6e96 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/flux/factory.py @@ -0,0 +1,26 @@ +# src/numerics/flux/factory.py +from core.registry import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + + @staticmethod + def get_available_types(): + """获取所有可用的重构器类型""" + from core.registry import BaseFactory + return BaseFactory.get_available_components('flux') + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/flux/rusanov.py b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/flux/rusanov.py new file mode 100644 index 00000000..56daa699 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/flux/rusanov.py @@ -0,0 +1,25 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量,使用 Equation 解耦物理参数""" + + def compute(self, q_face_left, q_face_right, flux): + # 从 cfd 获取 equation(与 Julia 对齐) + eq = self.cfd.problem.physical_system().equation + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = eq.wave_speed() + c_R = eq.wave_speed() + # 通过 equation 计算通量和波速 + F_L = eq.flux(u_L) + F_R = eq.flux(u_R) + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/reconstructor/__init__.py new file mode 100644 index 00000000..6b0a3d9b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/reconstructor/__init__.py @@ -0,0 +1,21 @@ +# src/numerics/reconstructor/__init__.py +""" +数值重构模块 +提供ENO、WENO等界面值重构方法 +""" + +from .base import Reconstructor +from .factory import ReconstructorFactory + +# 导入具体实现以触发注册 +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from .weno5 import Weno5Reconstructor + +__all__ = [ + 'Reconstructor', + 'ReconstructorFactory', + 'EnoReconstructor', + 'Weno3Reconstructor', + 'Weno5Reconstructor', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/reconstructor/base.py b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/reconstructor/base.py new file mode 100644 index 00000000..de00327c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def compute_face_values(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/reconstructor/eno.py b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/reconstructor/eno.py new file mode 100644 index 00000000..60699adf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/reconstructor/eno.py @@ -0,0 +1,96 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.initialize(self.config.spatial_order, self.domain.ntcells) + + def initialize(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def compute_face_values(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/reconstructor/factory.py b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/reconstructor/factory.py new file mode 100644 index 00000000..1f297eaa --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/reconstructor/factory.py @@ -0,0 +1,20 @@ +# src/numerics/reconstructor/factory.py +from core.registry import BaseFactory + +class ReconstructorFactory: + @staticmethod + def create(cfd): + config = cfd.config + scheme = config.recon_scheme.lower() + + if scheme.startswith("weno"): + if scheme == "weno": + order = getattr(config, 'spatial_order', None) + scheme = f"weno{order}" + return BaseFactory.create_component('reconstructor', scheme, cfd) + + @staticmethod + def get_available_types(): + """获取所有可用的重构器类型""" + from core.registry import BaseFactory + return BaseFactory.get_available_components('reconstructor') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/reconstructor/weno3.py new file mode 100644 index 00000000..f45eb35f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/reconstructor/weno3.py @@ -0,0 +1,62 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def __init__(self, cfd): + self.cfd = cfd + + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/reconstructor/weno5.py b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/reconstructor/weno5.py new file mode 100644 index 00000000..9d665275 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/reconstructor/weno5.py @@ -0,0 +1,71 @@ +# reconstructor/weno5.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno5') +class Weno5Reconstructor(Reconstructor): + def __init__(self, cfd): + self.cfd = cfd + + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3, v4, v5) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3, v4, v5) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 1.0/10.0 + d1 = 3.0/5.0 + d2 = 3.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = 1.0/3.0*v1-7.0/6.0*v2+11.0/6.0*v3 # r=2 + q1 = -1.0/6.0*v2+5.0/6.0*v3+1.0/3.0*v4 # r=1 + q2 = 1.0/3.0*v3+5.0/6.0*v4-1.0/6.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 3.0/10.0 + d1 = 3.0/5.0 + d2 = 1.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = -1.0/6.0*v1+5.0/6.0*v2+1.0/3.0*v3 # r=2 + q1 = 1.0/3.0*v2+5.0/6.0*v3-1.0/6.0*v4 # r=1 + q2 = 11.0/6.0*v3-7.0/6.0*v4+1.0/3.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/residual.py b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/residual.py new file mode 100644 index 00000000..2f96f05b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/residual.py @@ -0,0 +1,39 @@ +# src/numerics/residual.py +from numerics.flux.factory import FluxCalculatorFactory +from numerics.reconstructor.factory import ReconstructorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + + self.reconstructor = ReconstructorFactory.create(cfd) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._compute_face_values() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _compute_face_values(self): + """私有方法:界面值重建""" + self.reconstructor.compute_face_values(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/time_integration/__init__.py b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/time_integration/__init__.py new file mode 100644 index 00000000..9c127c77 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/time_integration/__init__.py @@ -0,0 +1,21 @@ +# src/numerics/time_integration/__init__.py +""" +时间积分模块 +提供显式Runge-Kutta方法 +""" + +from .base import TimeIntegrator +from .factory import TimeIntegratorFactory + +# 导入具体实现以触发注册 +from .rk1 import RK1Integrator +from .rk2 import RK2Integrator +from .rk3 import RK3Integrator + +__all__ = [ + 'TimeIntegrator', + 'TimeIntegratorFactory', + 'RK1Integrator', + 'RK2Integrator', + 'RK3Integrator', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/time_integration/base.py b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/time_integration/base.py new file mode 100644 index 00000000..0cdf29a4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/time_integration/base.py @@ -0,0 +1,27 @@ +# time_integration/base.py + +from abc import ABC, abstractmethod + +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + + @abstractmethod + def step(self, dt): + pass + + def compute_residual(self): + """计算残差(委托给 Cfd)""" + self.cfd.compute_residual() + + def apply_boundary(self): + """应用边界条件(委托给 Cfd)""" + self.cfd.apply_boundary() + + def map_idx(self, i): + """物理网格索引 → 残差数组索引""" + return i - self.domain.ist \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/time_integration/factory.py b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/time_integration/factory.py new file mode 100644 index 00000000..5e6f9779 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/time_integration/factory.py @@ -0,0 +1,15 @@ +# src/numerics/time_integration/factory.py +from core.registry import BaseFactory + +class TimeIntegratorFactory: + @staticmethod + def create(cfd) -> 'TimeIntegrator': + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) + + @staticmethod + def get_available_types(): + """获取所有可用的重构器类型""" + from core.registry import BaseFactory + return BaseFactory.get_available_components('integrator') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/time_integration/rk1.py b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/time_integration/rk1.py new file mode 100644 index 00000000..b4c8a021 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/time_integration/rk1.py @@ -0,0 +1,15 @@ +# time_integration/rk1.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/time_integration/rk2.py b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/time_integration/rk2.py new file mode 100644 index 00000000..6d2be304 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/time_integration/rk2.py @@ -0,0 +1,29 @@ +# time_integration/rk2.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + 0.5 * self.solution.un[i] + + 0.5 * self.solution.u[i] + + 0.5 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/time_integration/rk3.py b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/time_integration/rk3.py new file mode 100644 index 00000000..c70791e1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/numerics/time_integration/rk3.py @@ -0,0 +1,43 @@ +# time_integration/rk3.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # Stage 1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # Stage 2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = ( + 0.75 * self.solution.un[i] + + 0.25 * self.solution.u[i] + + 0.25 * dt * self.solution.res[j] + ) + self.solution.u[:] = u2 + self.apply_boundary() + + # Stage 3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + c1 * self.solution.un[i] + + c2 * self.solution.u[i] + + c3 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/physics/__init__.py b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/physics/equations/__init__.py b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/equations/__init__.py new file mode 100644 index 00000000..14b82117 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/equations/__init__.py @@ -0,0 +1,15 @@ +# src/physics/equations/__init__.py +""" +物理方程模块 +提供各种控制方程的实现 +""" + +from .base import Equation +from .linear_advection import LinearAdvectionEquation +from .euler import EulerEquation + +__all__ = [ + 'Equation', + 'LinearAdvectionEquation', + 'EulerEquation', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/physics/equations/base.py b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/equations/base.py new file mode 100644 index 00000000..05833e74 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/equations/base.py @@ -0,0 +1,60 @@ +# src/physics/equations/base.py +""" +物理方程抽象基类 +所有控制方程必须继承此类 +""" + +from abc import ABC, abstractmethod +import numpy as np + +class Equation(ABC): + """ + 控制方程抽象基类 + 所有物理方程(线性对流、Euler、MHD)必须继承此类 + """ + + @property + @abstractmethod + def num_equations(self) -> int: + """返回方程组变量数(标量=1,Euler=3,MHD=8)""" + pass + + @abstractmethod + def flux(self, u): + """ + 计算通量函数 f(u) + :param u: 状态向量 (num_equations,) + :return: 通量向量 (num_equations,) + """ + pass + + @abstractmethod + def max_wave_speed(self, u): + """ + 计算最大波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + @abstractmethod + def wave_speed(self): + """ + 计算波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + def exact_solution(self, x, t, initial_condition_func): + """ + 可选:提供解析解 + 子类可重写;若无解析解,调用方应捕获 NotImplementedError + :param x: 空间坐标数组 (ncells,) + :param t: 时间 + :param initial_condition_func: 初值函数 u0(x) -> (num_equations, ncells) + :return: 解 u(x,t) -> (num_equations, ncells) + """ + raise NotImplementedError( + f"Equation '{self.__class__.__name__}' does not provide an exact solution." + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/physics/equations/euler.py b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/equations/euler.py new file mode 100644 index 00000000..0b69c2d7 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/equations/euler.py @@ -0,0 +1,23 @@ +# src/physics/equations/euler.py +""" +欧拉方程(占位文件,未来实现) +""" +from .base import Equation + +class EulerEquation(Equation): + """欧拉方程:质量、动量、能量守恒""" + def __init__(self, gamma=1.4): + self.gamma = gamma + + @property + def num_equations(self) -> int: + return 3 + + def flux(self, u): + raise NotImplementedError("欧拉方程待实现") + + def max_wave_speed(self, u): + raise NotImplementedError("欧拉方程待实现") + + def wave_speed(self): + raise NotImplementedError("欧拉方程待实现") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/physics/equations/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/equations/linear_advection.py new file mode 100644 index 00000000..949c4a0a --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/equations/linear_advection.py @@ -0,0 +1,42 @@ +# src/physics/equations/linear_advection.py +""" +线性对流方程: u_t + c * u_x = 0 +支持标量(num_equations=1) +""" + +import numpy as np +from .base import Equation +from core.registry import register_component + +@register_component('equation', 'linear_advection') +class LinearAdvectionEquation(Equation): + def __init__(self, wave_speed: float): + self.c = wave_speed + + @property + def num_equations(self) -> int: + return 1 + + def flux(self, u): + """f(u) = c * u""" + return self.c * u + + def max_wave_speed(self, u): + """最大波速 = |c|""" + return abs(self.c) + + def wave_speed(self): + return self.c + + def exact_solution(self, x, t, initial_condition_func): + """ + 解析解: u(x, t) = u0(x - c * t) + 支持周期边界 + """ + if len(x) == 0: + return np.zeros((1, 0)) + + L = x[-1] - x[0] # 假设均匀周期网格 + x_shifted = (x - self.c * t + L) % L + u0 = initial_condition_func(x_shifted) # (1, ncells) + return u0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/physics/initial_conditions/__init__.py b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/initial_conditions/__init__.py new file mode 100644 index 00000000..94b0ad80 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/initial_conditions/__init__.py @@ -0,0 +1,16 @@ +# src/physics/initial_conditions/__init__.py +from .base import InitialCondition +from .factory import InitialConditionFactory + +# 导入具体类以触发注册 +from .step import StepFunctionIC +from .sine import SineWaveIC +from .gaussian import GaussianPulseIC + +__all__ = [ + 'InitialCondition', + 'InitialConditionFactory', + 'StepFunctionIC', + 'SineWaveIC', + 'GaussianPulseIC', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/physics/initial_conditions/base.py b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/initial_conditions/base.py new file mode 100644 index 00000000..8a33a0d5 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/initial_conditions/base.py @@ -0,0 +1,25 @@ +# src/initial_conditions/base.py +from abc import ABC, abstractmethod +import numpy as np + +class InitialCondition(ABC): + """初始条件抽象基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + """内部辅助方法:将值应用到物理区域""" + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/physics/initial_conditions/factory.py b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/initial_conditions/factory.py new file mode 100644 index 00000000..2a7558b2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/initial_conditions/factory.py @@ -0,0 +1,24 @@ +# src/initial_conditions/factory.py +from core.registry import BaseFactory +from .base import InitialCondition + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + config: 配置对象,包含ic_type属性 + + Returns: + 初始条件实例 + """ + return BaseFactory.create_component('initial_condition', config.ic_type, config) + + @classmethod + def get_available_types(cls): + """获取所有可用的初始条件类型""" + return BaseFactory.get_available_components('initial_condition') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/physics/initial_conditions/gaussian.py b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/initial_conditions/gaussian.py new file mode 100644 index 00000000..1dad463b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/initial_conditions/gaussian.py @@ -0,0 +1,16 @@ +# src/initial_conditions/gaussian.py +import numpy as np +from .base import InitialCondition +from core.registry import register_component + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/physics/initial_conditions/sine.py b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/initial_conditions/sine.py new file mode 100644 index 00000000..c061fa5a --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/initial_conditions/sine.py @@ -0,0 +1,15 @@ +# src/initial_conditions/sine.py +import numpy as np +from .base import InitialCondition +from core.registry import register_component + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/physics/initial_conditions/step.py b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/initial_conditions/step.py new file mode 100644 index 00000000..ff1120b2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/initial_conditions/step.py @@ -0,0 +1,17 @@ +# src/initial_conditions/step.py +import numpy as np +from .base import InitialCondition +from core.registry import register_component + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/physics/problems/__init__.py b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/problems/__init__.py new file mode 100644 index 00000000..e9d32905 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/problems/__init__.py @@ -0,0 +1,14 @@ +# src/physics/problems/__init__.py +""" +问题定义模块 +""" + +from .base import Problem +from .factory import ProblemFactory +from .linear_advection import LinearAdvectionProblem + +__all__ = [ + 'Problem', + 'ProblemFactory', + 'LinearAdvectionProblem', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/physics/problems/base.py b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/problems/base.py new file mode 100644 index 00000000..ddd8ddc4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/problems/base.py @@ -0,0 +1,42 @@ +# src/physics/problems/base.py +""" +问题定义抽象基类 +每个具体问题(如线性对流、Sod 激波管)应继承此类 +""" + +from abc import ABC, abstractmethod + +class Problem(ABC): + """ + 抽象问题基类 + """ + def __init__(self, config): + self.config = config + self._initial_condition = None + self._physical_system = None + + @abstractmethod + def initial_condition(self): + """返回 InitialCondition 实例""" + pass + + @abstractmethod + def physical_system(self): + """返回 PhysicalSystem 实例""" + pass + + def exact_solution(self, cfd): + """ + 计算解析解(委托给物理系统) + """ + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + ic = self.initial_condition() + system = self.physical_system() + + u_exact = system.exact_solution(x, t, ic) + return u_exact[0] # 返回标量数组 (ncells,) 以兼容现有绘图逻辑 + + @property + def name(self): + return self.__class__.__name__ \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/physics/problems/factory.py b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/problems/factory.py new file mode 100644 index 00000000..b3e3563f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/problems/factory.py @@ -0,0 +1,28 @@ +# src/physics/problems/factory.py +""" +问题工厂 +""" + +from core.registry import BaseFactory + +class ProblemFactory: + """问题工厂""" + + @staticmethod + def create(problem_type: str, config): + """ + 创建问题实例 + + Args: + problem_type: 问题类型,如 'linear_advection' + config: 配置对象 + + Returns: + 问题实例 + """ + return BaseFactory.create_component('problem', problem_type, config) + + @staticmethod + def get_available_types(): + """获取所有可用的问题类型""" + return BaseFactory.get_available_components('problem') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/physics/problems/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/problems/linear_advection.py new file mode 100644 index 00000000..f19ea53a --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/problems/linear_advection.py @@ -0,0 +1,36 @@ +# src/physics/problems/linear_advection.py +""" +线性对流问题 +""" + +from .base import Problem +from physics.equations.linear_advection import LinearAdvectionEquation +from physics.systems.linear_advection_system import LinearAdvectionSystem +from physics.initial_conditions.factory import InitialConditionFactory +from core.registry import register_component + +@register_component('problem', 'linear_advection') +class LinearAdvectionProblem(Problem): + """ + 线性对流问题:u_t + c u_x = 0 + 使用周期边界 + 任意初始条件 + """ + + def __init__(self, config): + super().__init__(config) + + def initial_condition(self): + """返回初始条件实例""" + if self._initial_condition is None: + from physics.initial_conditions.factory import InitialConditionFactory + self._initial_condition = InitialConditionFactory.create(self.config) + return self._initial_condition + + def physical_system(self): + """返回物理系统实例""" + if self._physical_system is None: + # 创建方程 + equation = LinearAdvectionEquation(wave_speed=self.config.wave_speed) + # 创建系统 + self._physical_system = LinearAdvectionSystem(equation) + return self._physical_system \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/physics/systems/__init__.py b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/systems/__init__.py new file mode 100644 index 00000000..bd22409c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/systems/__init__.py @@ -0,0 +1,15 @@ +# src/physics/systems/__init__.py +""" +物理系统模块 +组合方程和解析解逻辑 +""" + +from .base import PhysicalSystem +from .factory import PhysicalSystemFactory +from .linear_advection_system import LinearAdvectionSystem + +__all__ = [ + 'PhysicalSystem', + 'PhysicalSystemFactory', + 'LinearAdvectionSystem', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/physics/systems/base.py b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/systems/base.py new file mode 100644 index 00000000..957212d0 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/systems/base.py @@ -0,0 +1,33 @@ +# src/physics/systems/base.py +""" +物理系统基类 +将方程与解析解等逻辑组合 +""" + +from abc import ABC, abstractmethod + +class PhysicalSystem(ABC): + """物理系统抽象基类""" + + def __init__(self, equation): + self.equation = equation + + @abstractmethod + def exact_solution(self, x, t, initial_condition): + """ + 计算给定初始条件的解析解 + :param x: 空间坐标数组 + :param t: 时间 + :param initial_condition: 初始条件对象 + :return: 解析解数组 + """ + pass + + @property + def wave_speed(self): + """获取波速(委托给方程)""" + return self.equation.wave_speed() + + def flux(self, u): + """计算通量(委托给方程)""" + return self.equation.flux(u) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/physics/systems/factory.py b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/systems/factory.py new file mode 100644 index 00000000..6fcc754e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/systems/factory.py @@ -0,0 +1,28 @@ +# src/physics/systems/factory.py +""" +物理系统工厂 +""" + +from core.registry import BaseFactory + +class PhysicalSystemFactory: + """物理系统工厂""" + + @staticmethod + def create(system_type: str, **kwargs): + """ + 创建物理系统实例 + + Args: + system_type: 系统类型,如 'linear_advection' + **kwargs: 传递给方程的参数 + + Returns: + 物理系统实例 + """ + return BaseFactory.create_component('system', system_type, **kwargs) + + @staticmethod + def get_available_types(): + """获取所有可用的系统类型""" + return BaseFactory.get_available_components('system') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/physics/systems/linear_advection_system.py b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/systems/linear_advection_system.py new file mode 100644 index 00000000..0f3a4901 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/physics/systems/linear_advection_system.py @@ -0,0 +1,36 @@ +# src/physics/systems/linear_advection_system.py +""" +线性对流物理系统 +""" + +from .base import PhysicalSystem +from core.registry import register_component +import numpy as np + +@register_component('system', 'linear_advection') +class LinearAdvectionSystem(PhysicalSystem): + """线性对流系统""" + + def exact_solution(self, x, t, initial_condition): + """ + 计算线性对流的解析解 + :param x: 空间坐标数组 + :param t: 时间 + :param initial_condition: 初始条件对象 + :return: 解析解数组 + """ + if len(x) == 0: + return np.zeros((1, 0)) + + L = x[-1] - x[0] # 假设均匀周期网格 + c = self.equation.wave_speed() + x_shifted = (x - c * t + L) % L + + # 调用初始条件的 evaluate_at 方法 + u0 = initial_condition.evaluate_at(x_shifted) + + # 确保返回形状正确 + if u0.ndim == 1: + u0 = u0[np.newaxis, :] # (1, ncells) + + return u0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02g/src/visualization/plotter.py b/example/1d-linear-convection/weno3/python-v1/02g/src/visualization/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02g/src/visualization/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/example/linear_advection/run_eno_weno.py b/example/1d-linear-convection/weno3/python-v1/02h/example/linear_advection/run_eno_weno.py new file mode 100644 index 00000000..b2282264 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/example/linear_advection/run_eno_weno.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +# example/run_eno_weno.py + +import sys +import os + +# 获取项目的绝对路径 +current_dir = os.path.dirname(os.path.abspath(__file__)) +project_root = os.path.abspath(os.path.join(current_dir, '..', '..')) + +print(f"current_dir={current_dir}") +print(f"project_root={project_root}") + +#src_path = os.path.join(os.path.dirname(__file__), '..', 'src') +# 新的路径(在 example/linear_advection/ 目录下): +src_path = os.path.join(os.path.dirname(__file__), '..', '..', 'src') +if src_path not in sys.path: + sys.path.insert(0, src_path) + + + +from core.solver import Cfd +from infrastructure.config import CfdConfig +from infrastructure.mesh import Mesh +from visualization.plotter import plot_eno_weno_comparison, CFDPlotter +from physics.problems.linear_advection import LinearAdvectionProblem + +def performEnoWenoAnalysisBAK(): + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO3 求解 + print("Running ENO3 solver...") + config_eno3 = CfdConfig() + config_eno3.with_reconstruction("eno", 3) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno3 = LinearAdvectionProblem(config_eno3) + cfd_eno3 = Cfd(problem_eno3, mesh) # ← 注入 problem + cfd_eno3.run() + + # 3. 配置并运行 WENO3 求解 + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno3 = LinearAdvectionProblem(config_weno3) + cfd_weno3 = Cfd(problem_weno3, mesh) # ← 注入 problem + cfd_weno3.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" + ) + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + mesh = Mesh() + + # 2. 配置并运行 ENO5 求解 + print("Running ENO5 solver...") + config_eno5 = CfdConfig() + config_eno5.with_reconstruction("eno", 5) + config_eno5.dt = 0.0025 + config_eno5.rk_order = 2 + + # ✅ 创建 Problem 实例(替代直接传 config) + problem_eno5 = LinearAdvectionProblem(config_eno5) + cfd_eno5 = Cfd(problem_eno5, mesh) # ← 注入 problem + cfd_eno5.run() + + # 3. 配置并运行 WENO5求解 + print("Running WENO5 solver...") + config_weno5 = CfdConfig() + config_weno5.with_reconstruction("weno", 5) + config_weno5.dt = 0.0025 + config_weno5.rk_order = 2 + + # ✅ 创建另一个 Problem 实例 + problem_weno5 = LinearAdvectionProblem(config_weno5) + cfd_weno5 = Cfd(problem_weno5, mesh) # ← 注入 problem + cfd_weno5.run() + + # 4. 绘制对比图(完全不变) + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno5.result, + weno_result=cfd_weno5.result, + save_path="eno_weno_comparison.png" + ) + + +if __name__ == "__main__": + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/core/registry.py b/example/1d-linear-convection/weno3/python-v1/02h/src/core/registry.py new file mode 100644 index 00000000..dad44ec7 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/core/registry.py @@ -0,0 +1,83 @@ +# src/core/registry.py +""" +统一注册系统 + 通用工厂 +- ComponentRegistry: 组件注册表 +- register_component: 装饰器 +- BaseFactory: 通用工厂类 +""" + +from typing import Dict, Type, Any + + +# ==================== 1. 注册表核心 ==================== +class ComponentRegistry: + """组件注册表""" + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True + + @classmethod + def set_verbose(cls, verbose: bool): + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + #print(f"ComponentRegistry register cls={cls}") + #print(f"ComponentRegistry register name={name}") + #print(f"ComponentRegistry register component_class={component_class}") + if category not in cls._registries: + cls._registries[category] = {} + if name in cls._registries[category]: + if cls._registries[category][name] != component_class and cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + if category not in cls._registries: + raise ValueError(f"❌ 未知类别: {category} (可用: {list(cls._registries.keys())})") + if name not in cls._registries[category]: + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {list(cls._registries[category].keys())})") + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) for cat, comps in cls._registries.items()} + + +# ==================== 2. 装饰器 ==================== +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + #print(f"register_component decorator name={name}") + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + +# ==================== 3. 通用工厂 ==================== +class BaseFactory: + """通用工厂基类""" + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + name_lower = name.lower() + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + available = ComponentRegistry.list_all().get(category, []) + if available: + error_msg = f"不支持的 {category} 类型 '{name}'。可用类型:{available}" + else: + error_msg = f"不支持的 {category} 类型 '{name}'(无已注册组件)" + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/core/solver.py b/example/1d-linear-convection/weno3/python-v1/02h/src/core/solver.py new file mode 100644 index 00000000..d41de3ee --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/core/solver.py @@ -0,0 +1,67 @@ +# src/core/solver.py + +from infrastructure.config import CfdConfig +from infrastructure.result import ResultAssembler +from infrastructure.boundary.factory import BoundaryConditionFactory +from infrastructure.mesh import Mesh +from infrastructure.domain import Domain +from infrastructure.solution import Solution +from physics.problems.base import Problem +from physics.initial_conditions import InitialConditionFactory +from numerics.time_integration.factory import TimeIntegratorFactory +from numerics.residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, problem: Problem, mesh): + self.problem = problem + self.config = problem.config + self.domain = Domain(problem.config, mesh) + self.solution = Solution(problem.config, self.domain) + + self.initial_condition = InitialConditionFactory.create(self.config) + self.boundary_condition = BoundaryConditionFactory.create(self) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + + # ==================== 公共接口(供 TimeIntegrator 调用) ==================== + def compute_residual(self): + """计算物理残差(封装重建→通量→散度)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件""" + self.boundary_condition.apply(self.solution.u) + + # ==================== 初始化 ==================== + def initialize(self): + """初始化全场:先 IC,再 BC,最后同步 old field""" + self.initial_condition.apply(self.solution) + # 应用边界条件到初始场 + self.apply_boundary() + # 同步 old field + self.solution.update_old_field() + + + def run(self): + self.initialize() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + self.result = ResultAssembler.assemble(self) + return self.result["numerical"] + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/__init__.py b/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/__init__.py new file mode 100644 index 00000000..e476afcb --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/__init__.py @@ -0,0 +1,18 @@ +# src/infrastructure/__init__.py +""" +基础设施模块 +包含网格、域、解、边界条件等基础组件 +""" + +from .mesh import Mesh +from .domain import Domain +from .solution import Solution +from .boundary import BoundaryCondition, BoundaryConditionFactory + +__all__ = [ + 'Mesh', + 'Domain', + 'Solution', + 'BoundaryCondition', + 'BoundaryConditionFactory', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/boundary/__init__.py b/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/boundary/__init__.py new file mode 100644 index 00000000..20120e8a --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/boundary/__init__.py @@ -0,0 +1,21 @@ +# src/boundary/__init__.py +""" +边界条件模块 +提供各种边界条件实现和工厂创建接口 +""" + +from .base import BoundaryCondition +from .factory import BoundaryConditionFactory + +# 导入具体类以触发注册 +from .periodic import PeriodicBoundary +from .dirichlet import DirichletBoundary +from .neumann import NeumannBoundary + +__all__ = [ + 'BoundaryCondition', + 'BoundaryConditionFactory', + 'PeriodicBoundary', + 'DirichletBoundary', + 'NeumannBoundary', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/boundary/base.py b/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/boundary/base.py new file mode 100644 index 00000000..cddf649d --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/boundary/base.py @@ -0,0 +1,18 @@ +# src/boundary/base.py +from abc import ABC, abstractmethod + +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/boundary/dirichlet.py b/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/boundary/dirichlet.py new file mode 100644 index 00000000..1eff3699 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/boundary/dirichlet.py @@ -0,0 +1,27 @@ +# src/boundary/dirichlet.py +from .base import BoundaryCondition +from core.registry import register_component + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/boundary/factory.py b/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/boundary/factory.py new file mode 100644 index 00000000..4bd7ae55 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/boundary/factory.py @@ -0,0 +1,24 @@ +# src/boundary/factory.py +from core.registry import BaseFactory + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + + @staticmethod + def get_available_types(): + """获取所有可用的边界条件类型""" + return BaseFactory.get_available_components('boundary') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/boundary/neumann.py b/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/boundary/neumann.py new file mode 100644 index 00000000..6eb72f64 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/boundary/neumann.py @@ -0,0 +1,23 @@ +# src/boundary/neumann.py +from .base import BoundaryCondition +from core.registry import register_component + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/boundary/periodic.py b/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/boundary/periodic.py new file mode 100644 index 00000000..bd601d95 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/boundary/periodic.py @@ -0,0 +1,19 @@ +# src/boundary/periodic.py +from .base import BoundaryCondition +from core.registry import register_component + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/config.py b/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/config.py new file mode 100644 index 00000000..58a952eb --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/config.py @@ -0,0 +1,45 @@ +# src/infrastructure/config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + #self.ic_type = "sin" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # "rusanov", "engquist-osher" + #self.flux_type = "engquist-osher" # "rusanov", "engquist-osher" + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + print(f"scheme={scheme}") + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/domain.py b/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/domain.py new file mode 100644 index 00000000..05402f4c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/domain.py @@ -0,0 +1,56 @@ +# src/infrastructure/domain.py +from .mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/mesh.py b/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/result.py b/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/result.py new file mode 100644 index 00000000..6d70839b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/result.py @@ -0,0 +1,25 @@ +# src/infrastructure/result.py + +class ResultAssembler: + @staticmethod + def assemble(cfd: 'Cfd') -> dict: + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied].copy() + + # ✅ 从 problem 获取解析解(唯一正确路径) + try: + analytical = cfd.problem.exact_solution(cfd) + except NotImplementedError: + analytical = None # 或 np.full_like(u_numerical, np.nan) + + return { + "x": cfd.domain.mesh.xcc, + "numerical": u_numerical, + "analytical": analytical, + "config": { + "scheme": cfd.config.recon_scheme, + "order": cfd.config.spatial_order, + "rk_order": cfd.config.rk_order, + "final_time": cfd.config.final_time, + "problem": cfd.problem.name, + } + } \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/solution.py b/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/solution.py new file mode 100644 index 00000000..c3a00dd5 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/infrastructure/solution.py @@ -0,0 +1,32 @@ +# src/infrastructure/solution.py +import numpy as np + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/__init__.py b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/__init__.py new file mode 100644 index 00000000..4cfdcc73 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/__init__.py @@ -0,0 +1,27 @@ +# src/numerics/__init__.py +""" +数值方法模块 +包含重构器、通量计算、时间积分等数值方法 +""" + +from .residual import ResidualCalculator +from .reconstructor import Reconstructor, ReconstructorFactory +from .flux import InviscidFluxCalculator, FluxCalculatorFactory +from .time_integration import TimeIntegrator, TimeIntegratorFactory + +__all__ = [ + # 残差计算 + 'ResidualCalculator', + + # 重构器 + 'Reconstructor', + 'ReconstructorFactory', + + # 通量计算 + 'InviscidFluxCalculator', + 'FluxCalculatorFactory', + + # 时间积分 + 'TimeIntegrator', + 'TimeIntegratorFactory', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/flux/__init__.py b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/flux/__init__.py new file mode 100644 index 00000000..c39d6253 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/flux/__init__.py @@ -0,0 +1,19 @@ +# src/numerics/flux/__init__.py +""" +通量计算模块 +提供Rusanov、Engquist-Osher等通量计算方法 +""" + +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 导入具体实现以触发注册 +from .rusanov import RusanovFluxCalculator +from .engquist_osher import EngquistOsherFluxCalculator + +__all__ = [ + 'InviscidFluxCalculator', + 'FluxCalculatorFactory', + 'RusanovFluxCalculator', + 'EngquistOsherFluxCalculator', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/flux/base.py b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/flux/engquist_osher.py new file mode 100644 index 00000000..16dbce82 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/flux/engquist_osher.py @@ -0,0 +1,20 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + eq = self.cfd.problem.physical_system().equation + for i in range(self.mesh.nnodes): + c = eq.wave_speed() + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/flux/factory.py b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/flux/factory.py new file mode 100644 index 00000000..c35f6e96 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/flux/factory.py @@ -0,0 +1,26 @@ +# src/numerics/flux/factory.py +from core.registry import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + + @staticmethod + def get_available_types(): + """获取所有可用的重构器类型""" + from core.registry import BaseFactory + return BaseFactory.get_available_components('flux') + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/flux/rusanov.py b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/flux/rusanov.py new file mode 100644 index 00000000..56daa699 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/flux/rusanov.py @@ -0,0 +1,25 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from core.registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量,使用 Equation 解耦物理参数""" + + def compute(self, q_face_left, q_face_right, flux): + # 从 cfd 获取 equation(与 Julia 对齐) + eq = self.cfd.problem.physical_system().equation + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = eq.wave_speed() + c_R = eq.wave_speed() + # 通过 equation 计算通量和波速 + F_L = eq.flux(u_L) + F_R = eq.flux(u_R) + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/reconstructor/__init__.py new file mode 100644 index 00000000..6b0a3d9b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/reconstructor/__init__.py @@ -0,0 +1,21 @@ +# src/numerics/reconstructor/__init__.py +""" +数值重构模块 +提供ENO、WENO等界面值重构方法 +""" + +from .base import Reconstructor +from .factory import ReconstructorFactory + +# 导入具体实现以触发注册 +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from .weno5 import Weno5Reconstructor + +__all__ = [ + 'Reconstructor', + 'ReconstructorFactory', + 'EnoReconstructor', + 'Weno3Reconstructor', + 'Weno5Reconstructor', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/reconstructor/base.py b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/reconstructor/base.py new file mode 100644 index 00000000..de00327c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def compute_face_values(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/reconstructor/eno.py b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/reconstructor/eno.py new file mode 100644 index 00000000..60699adf --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/reconstructor/eno.py @@ -0,0 +1,96 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.initialize(self.config.spatial_order, self.domain.ntcells) + + def initialize(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def compute_face_values(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/reconstructor/factory.py b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/reconstructor/factory.py new file mode 100644 index 00000000..1f297eaa --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/reconstructor/factory.py @@ -0,0 +1,20 @@ +# src/numerics/reconstructor/factory.py +from core.registry import BaseFactory + +class ReconstructorFactory: + @staticmethod + def create(cfd): + config = cfd.config + scheme = config.recon_scheme.lower() + + if scheme.startswith("weno"): + if scheme == "weno": + order = getattr(config, 'spatial_order', None) + scheme = f"weno{order}" + return BaseFactory.create_component('reconstructor', scheme, cfd) + + @staticmethod + def get_available_types(): + """获取所有可用的重构器类型""" + from core.registry import BaseFactory + return BaseFactory.get_available_components('reconstructor') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/reconstructor/weno3.py new file mode 100644 index 00000000..f45eb35f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/reconstructor/weno3.py @@ -0,0 +1,62 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def __init__(self, cfd): + self.cfd = cfd + + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/reconstructor/weno5.py b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/reconstructor/weno5.py new file mode 100644 index 00000000..9d665275 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/reconstructor/weno5.py @@ -0,0 +1,71 @@ +# reconstructor/weno5.py +import numpy as np +from .base import Reconstructor +from core.registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno5') +class Weno5Reconstructor(Reconstructor): + def __init__(self, cfd): + self.cfd = cfd + + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3, v4, v5) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3, v4, v5 = u[i-2], u[i-1], u[i], u[i+1], u[i+2] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3, v4, v5) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 1.0/10.0 + d1 = 3.0/5.0 + d2 = 3.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = 1.0/3.0*v1-7.0/6.0*v2+11.0/6.0*v3 # r=2 + q1 = -1.0/6.0*v2+5.0/6.0*v3+1.0/3.0*v4 # r=1 + q2 = 1.0/3.0*v3+5.0/6.0*v4-1.0/6.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 3.0/10.0 + d1 = 3.0/5.0 + d2 = 1.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = -1.0/6.0*v1+5.0/6.0*v2+1.0/3.0*v3 # r=2 + q1 = 1.0/3.0*v2+5.0/6.0*v3-1.0/6.0*v4 # r=1 + q2 = 11.0/6.0*v3-7.0/6.0*v4+1.0/3.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/residual.py b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/residual.py new file mode 100644 index 00000000..2f96f05b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/residual.py @@ -0,0 +1,39 @@ +# src/numerics/residual.py +from numerics.flux.factory import FluxCalculatorFactory +from numerics.reconstructor.factory import ReconstructorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + + self.reconstructor = ReconstructorFactory.create(cfd) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._compute_face_values() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _compute_face_values(self): + """私有方法:界面值重建""" + self.reconstructor.compute_face_values(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/time_integration/__init__.py b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/time_integration/__init__.py new file mode 100644 index 00000000..9c127c77 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/time_integration/__init__.py @@ -0,0 +1,21 @@ +# src/numerics/time_integration/__init__.py +""" +时间积分模块 +提供显式Runge-Kutta方法 +""" + +from .base import TimeIntegrator +from .factory import TimeIntegratorFactory + +# 导入具体实现以触发注册 +from .rk1 import RK1Integrator +from .rk2 import RK2Integrator +from .rk3 import RK3Integrator + +__all__ = [ + 'TimeIntegrator', + 'TimeIntegratorFactory', + 'RK1Integrator', + 'RK2Integrator', + 'RK3Integrator', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/time_integration/base.py b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/time_integration/base.py new file mode 100644 index 00000000..0cdf29a4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/time_integration/base.py @@ -0,0 +1,27 @@ +# time_integration/base.py + +from abc import ABC, abstractmethod + +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + + @abstractmethod + def step(self, dt): + pass + + def compute_residual(self): + """计算残差(委托给 Cfd)""" + self.cfd.compute_residual() + + def apply_boundary(self): + """应用边界条件(委托给 Cfd)""" + self.cfd.apply_boundary() + + def map_idx(self, i): + """物理网格索引 → 残差数组索引""" + return i - self.domain.ist \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/time_integration/factory.py b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/time_integration/factory.py new file mode 100644 index 00000000..5e6f9779 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/time_integration/factory.py @@ -0,0 +1,15 @@ +# src/numerics/time_integration/factory.py +from core.registry import BaseFactory + +class TimeIntegratorFactory: + @staticmethod + def create(cfd) -> 'TimeIntegrator': + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) + + @staticmethod + def get_available_types(): + """获取所有可用的重构器类型""" + from core.registry import BaseFactory + return BaseFactory.get_available_components('integrator') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/time_integration/rk1.py b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/time_integration/rk1.py new file mode 100644 index 00000000..b4c8a021 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/time_integration/rk1.py @@ -0,0 +1,15 @@ +# time_integration/rk1.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/time_integration/rk2.py b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/time_integration/rk2.py new file mode 100644 index 00000000..6d2be304 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/time_integration/rk2.py @@ -0,0 +1,29 @@ +# time_integration/rk2.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + 0.5 * self.solution.un[i] + + 0.5 * self.solution.u[i] + + 0.5 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/time_integration/rk3.py b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/time_integration/rk3.py new file mode 100644 index 00000000..c70791e1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/numerics/time_integration/rk3.py @@ -0,0 +1,43 @@ +# time_integration/rk3.py + +from .base import TimeIntegrator +from core.registry import register_component + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # Stage 1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # Stage 2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = ( + 0.75 * self.solution.un[i] + + 0.25 * self.solution.u[i] + + 0.25 * dt * self.solution.res[j] + ) + self.solution.u[:] = u2 + self.apply_boundary() + + # Stage 3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = ( + c1 * self.solution.un[i] + + c2 * self.solution.u[i] + + c3 * dt * self.solution.res[j] + ) + self.apply_boundary() + self.solution.update_old_field() \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/physics/__init__.py b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/physics/equations/__init__.py b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/equations/__init__.py new file mode 100644 index 00000000..14b82117 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/equations/__init__.py @@ -0,0 +1,15 @@ +# src/physics/equations/__init__.py +""" +物理方程模块 +提供各种控制方程的实现 +""" + +from .base import Equation +from .linear_advection import LinearAdvectionEquation +from .euler import EulerEquation + +__all__ = [ + 'Equation', + 'LinearAdvectionEquation', + 'EulerEquation', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/physics/equations/base.py b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/equations/base.py new file mode 100644 index 00000000..05833e74 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/equations/base.py @@ -0,0 +1,60 @@ +# src/physics/equations/base.py +""" +物理方程抽象基类 +所有控制方程必须继承此类 +""" + +from abc import ABC, abstractmethod +import numpy as np + +class Equation(ABC): + """ + 控制方程抽象基类 + 所有物理方程(线性对流、Euler、MHD)必须继承此类 + """ + + @property + @abstractmethod + def num_equations(self) -> int: + """返回方程组变量数(标量=1,Euler=3,MHD=8)""" + pass + + @abstractmethod + def flux(self, u): + """ + 计算通量函数 f(u) + :param u: 状态向量 (num_equations,) + :return: 通量向量 (num_equations,) + """ + pass + + @abstractmethod + def max_wave_speed(self, u): + """ + 计算最大波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + @abstractmethod + def wave_speed(self): + """ + 计算波速(用于 CFL 条件) + :param u: 状态向量 + :return: 标量波速 + """ + pass + + def exact_solution(self, x, t, initial_condition_func): + """ + 可选:提供解析解 + 子类可重写;若无解析解,调用方应捕获 NotImplementedError + :param x: 空间坐标数组 (ncells,) + :param t: 时间 + :param initial_condition_func: 初值函数 u0(x) -> (num_equations, ncells) + :return: 解 u(x,t) -> (num_equations, ncells) + """ + raise NotImplementedError( + f"Equation '{self.__class__.__name__}' does not provide an exact solution." + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/physics/equations/euler.py b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/equations/euler.py new file mode 100644 index 00000000..0b69c2d7 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/equations/euler.py @@ -0,0 +1,23 @@ +# src/physics/equations/euler.py +""" +欧拉方程(占位文件,未来实现) +""" +from .base import Equation + +class EulerEquation(Equation): + """欧拉方程:质量、动量、能量守恒""" + def __init__(self, gamma=1.4): + self.gamma = gamma + + @property + def num_equations(self) -> int: + return 3 + + def flux(self, u): + raise NotImplementedError("欧拉方程待实现") + + def max_wave_speed(self, u): + raise NotImplementedError("欧拉方程待实现") + + def wave_speed(self): + raise NotImplementedError("欧拉方程待实现") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/physics/equations/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/equations/linear_advection.py new file mode 100644 index 00000000..949c4a0a --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/equations/linear_advection.py @@ -0,0 +1,42 @@ +# src/physics/equations/linear_advection.py +""" +线性对流方程: u_t + c * u_x = 0 +支持标量(num_equations=1) +""" + +import numpy as np +from .base import Equation +from core.registry import register_component + +@register_component('equation', 'linear_advection') +class LinearAdvectionEquation(Equation): + def __init__(self, wave_speed: float): + self.c = wave_speed + + @property + def num_equations(self) -> int: + return 1 + + def flux(self, u): + """f(u) = c * u""" + return self.c * u + + def max_wave_speed(self, u): + """最大波速 = |c|""" + return abs(self.c) + + def wave_speed(self): + return self.c + + def exact_solution(self, x, t, initial_condition_func): + """ + 解析解: u(x, t) = u0(x - c * t) + 支持周期边界 + """ + if len(x) == 0: + return np.zeros((1, 0)) + + L = x[-1] - x[0] # 假设均匀周期网格 + x_shifted = (x - self.c * t + L) % L + u0 = initial_condition_func(x_shifted) # (1, ncells) + return u0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/physics/initial_conditions/__init__.py b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/initial_conditions/__init__.py new file mode 100644 index 00000000..94b0ad80 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/initial_conditions/__init__.py @@ -0,0 +1,16 @@ +# src/physics/initial_conditions/__init__.py +from .base import InitialCondition +from .factory import InitialConditionFactory + +# 导入具体类以触发注册 +from .step import StepFunctionIC +from .sine import SineWaveIC +from .gaussian import GaussianPulseIC + +__all__ = [ + 'InitialCondition', + 'InitialConditionFactory', + 'StepFunctionIC', + 'SineWaveIC', + 'GaussianPulseIC', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/physics/initial_conditions/base.py b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/initial_conditions/base.py new file mode 100644 index 00000000..8a33a0d5 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/initial_conditions/base.py @@ -0,0 +1,25 @@ +# src/initial_conditions/base.py +from abc import ABC, abstractmethod +import numpy as np + +class InitialCondition(ABC): + """初始条件抽象基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + """内部辅助方法:将值应用到物理区域""" + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/physics/initial_conditions/factory.py b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/initial_conditions/factory.py new file mode 100644 index 00000000..2a7558b2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/initial_conditions/factory.py @@ -0,0 +1,24 @@ +# src/initial_conditions/factory.py +from core.registry import BaseFactory +from .base import InitialCondition + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + config: 配置对象,包含ic_type属性 + + Returns: + 初始条件实例 + """ + return BaseFactory.create_component('initial_condition', config.ic_type, config) + + @classmethod + def get_available_types(cls): + """获取所有可用的初始条件类型""" + return BaseFactory.get_available_components('initial_condition') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/physics/initial_conditions/gaussian.py b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/initial_conditions/gaussian.py new file mode 100644 index 00000000..1dad463b --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/initial_conditions/gaussian.py @@ -0,0 +1,16 @@ +# src/initial_conditions/gaussian.py +import numpy as np +from .base import InitialCondition +from core.registry import register_component + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/physics/initial_conditions/sine.py b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/initial_conditions/sine.py new file mode 100644 index 00000000..c061fa5a --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/initial_conditions/sine.py @@ -0,0 +1,15 @@ +# src/initial_conditions/sine.py +import numpy as np +from .base import InitialCondition +from core.registry import register_component + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/physics/initial_conditions/step.py b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/initial_conditions/step.py new file mode 100644 index 00000000..ff1120b2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/initial_conditions/step.py @@ -0,0 +1,17 @@ +# src/initial_conditions/step.py +import numpy as np +from .base import InitialCondition +from core.registry import register_component + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/physics/problems/__init__.py b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/problems/__init__.py new file mode 100644 index 00000000..e9d32905 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/problems/__init__.py @@ -0,0 +1,14 @@ +# src/physics/problems/__init__.py +""" +问题定义模块 +""" + +from .base import Problem +from .factory import ProblemFactory +from .linear_advection import LinearAdvectionProblem + +__all__ = [ + 'Problem', + 'ProblemFactory', + 'LinearAdvectionProblem', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/physics/problems/base.py b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/problems/base.py new file mode 100644 index 00000000..ddd8ddc4 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/problems/base.py @@ -0,0 +1,42 @@ +# src/physics/problems/base.py +""" +问题定义抽象基类 +每个具体问题(如线性对流、Sod 激波管)应继承此类 +""" + +from abc import ABC, abstractmethod + +class Problem(ABC): + """ + 抽象问题基类 + """ + def __init__(self, config): + self.config = config + self._initial_condition = None + self._physical_system = None + + @abstractmethod + def initial_condition(self): + """返回 InitialCondition 实例""" + pass + + @abstractmethod + def physical_system(self): + """返回 PhysicalSystem 实例""" + pass + + def exact_solution(self, cfd): + """ + 计算解析解(委托给物理系统) + """ + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + ic = self.initial_condition() + system = self.physical_system() + + u_exact = system.exact_solution(x, t, ic) + return u_exact[0] # 返回标量数组 (ncells,) 以兼容现有绘图逻辑 + + @property + def name(self): + return self.__class__.__name__ \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/physics/problems/factory.py b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/problems/factory.py new file mode 100644 index 00000000..b3e3563f --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/problems/factory.py @@ -0,0 +1,28 @@ +# src/physics/problems/factory.py +""" +问题工厂 +""" + +from core.registry import BaseFactory + +class ProblemFactory: + """问题工厂""" + + @staticmethod + def create(problem_type: str, config): + """ + 创建问题实例 + + Args: + problem_type: 问题类型,如 'linear_advection' + config: 配置对象 + + Returns: + 问题实例 + """ + return BaseFactory.create_component('problem', problem_type, config) + + @staticmethod + def get_available_types(): + """获取所有可用的问题类型""" + return BaseFactory.get_available_components('problem') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/physics/problems/linear_advection.py b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/problems/linear_advection.py new file mode 100644 index 00000000..f19ea53a --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/problems/linear_advection.py @@ -0,0 +1,36 @@ +# src/physics/problems/linear_advection.py +""" +线性对流问题 +""" + +from .base import Problem +from physics.equations.linear_advection import LinearAdvectionEquation +from physics.systems.linear_advection_system import LinearAdvectionSystem +from physics.initial_conditions.factory import InitialConditionFactory +from core.registry import register_component + +@register_component('problem', 'linear_advection') +class LinearAdvectionProblem(Problem): + """ + 线性对流问题:u_t + c u_x = 0 + 使用周期边界 + 任意初始条件 + """ + + def __init__(self, config): + super().__init__(config) + + def initial_condition(self): + """返回初始条件实例""" + if self._initial_condition is None: + from physics.initial_conditions.factory import InitialConditionFactory + self._initial_condition = InitialConditionFactory.create(self.config) + return self._initial_condition + + def physical_system(self): + """返回物理系统实例""" + if self._physical_system is None: + # 创建方程 + equation = LinearAdvectionEquation(wave_speed=self.config.wave_speed) + # 创建系统 + self._physical_system = LinearAdvectionSystem(equation) + return self._physical_system \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/physics/systems/__init__.py b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/systems/__init__.py new file mode 100644 index 00000000..bd22409c --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/systems/__init__.py @@ -0,0 +1,15 @@ +# src/physics/systems/__init__.py +""" +物理系统模块 +组合方程和解析解逻辑 +""" + +from .base import PhysicalSystem +from .factory import PhysicalSystemFactory +from .linear_advection_system import LinearAdvectionSystem + +__all__ = [ + 'PhysicalSystem', + 'PhysicalSystemFactory', + 'LinearAdvectionSystem', +] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/physics/systems/base.py b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/systems/base.py new file mode 100644 index 00000000..957212d0 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/systems/base.py @@ -0,0 +1,33 @@ +# src/physics/systems/base.py +""" +物理系统基类 +将方程与解析解等逻辑组合 +""" + +from abc import ABC, abstractmethod + +class PhysicalSystem(ABC): + """物理系统抽象基类""" + + def __init__(self, equation): + self.equation = equation + + @abstractmethod + def exact_solution(self, x, t, initial_condition): + """ + 计算给定初始条件的解析解 + :param x: 空间坐标数组 + :param t: 时间 + :param initial_condition: 初始条件对象 + :return: 解析解数组 + """ + pass + + @property + def wave_speed(self): + """获取波速(委托给方程)""" + return self.equation.wave_speed() + + def flux(self, u): + """计算通量(委托给方程)""" + return self.equation.flux(u) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/physics/systems/factory.py b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/systems/factory.py new file mode 100644 index 00000000..6fcc754e --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/systems/factory.py @@ -0,0 +1,28 @@ +# src/physics/systems/factory.py +""" +物理系统工厂 +""" + +from core.registry import BaseFactory + +class PhysicalSystemFactory: + """物理系统工厂""" + + @staticmethod + def create(system_type: str, **kwargs): + """ + 创建物理系统实例 + + Args: + system_type: 系统类型,如 'linear_advection' + **kwargs: 传递给方程的参数 + + Returns: + 物理系统实例 + """ + return BaseFactory.create_component('system', system_type, **kwargs) + + @staticmethod + def get_available_types(): + """获取所有可用的系统类型""" + return BaseFactory.get_available_components('system') \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/physics/systems/linear_advection_system.py b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/systems/linear_advection_system.py new file mode 100644 index 00000000..0f3a4901 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/physics/systems/linear_advection_system.py @@ -0,0 +1,36 @@ +# src/physics/systems/linear_advection_system.py +""" +线性对流物理系统 +""" + +from .base import PhysicalSystem +from core.registry import register_component +import numpy as np + +@register_component('system', 'linear_advection') +class LinearAdvectionSystem(PhysicalSystem): + """线性对流系统""" + + def exact_solution(self, x, t, initial_condition): + """ + 计算线性对流的解析解 + :param x: 空间坐标数组 + :param t: 时间 + :param initial_condition: 初始条件对象 + :return: 解析解数组 + """ + if len(x) == 0: + return np.zeros((1, 0)) + + L = x[-1] - x[0] # 假设均匀周期网格 + c = self.equation.wave_speed() + x_shifted = (x - c * t + L) % L + + # 调用初始条件的 evaluate_at 方法 + u0 = initial_condition.evaluate_at(x_shifted) + + # 确保返回形状正确 + if u0.ndim == 1: + u0 = u0[np.newaxis, :] # (1, ncells) + + return u0 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python-v1/02h/src/visualization/plotter.py b/example/1d-linear-convection/weno3/python-v1/02h/src/visualization/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python-v1/02h/src/visualization/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05/boundary.py b/example/1d-linear-convection/weno3/python/05/boundary.py new file mode 100644 index 00000000..2d8af5a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05/boundary.py @@ -0,0 +1,83 @@ +from abc import ABC, abstractmethod + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(可无限扩展) ---------------------- +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界(进口)固定值(从配置读取) + left_value = self.config.get("left_boundary_value", 1.0) + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # 右边界(出口)固定值(从配置读取) + right_value = self.config.get("right_boundary_value", 2.0) + for ig in range(nghosts): + u[ied + ig] = right_value + +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + +# ---------------------- 边界条件工厂(动态创建实例) ---------------------- +class BoundaryConditionFactory: + """边界条件工厂:根据配置创建对应边界条件实例""" + @staticmethod + def create(cfd): + # 从配置读取边界类型(支持多边界组合,1D暂用单一类型) + bc_type = cfd.config.boundary_type.lower() + + if bc_type == "periodic": + return PeriodicBoundary(cfd) + elif bc_type == "dirichlet": + return DirichletBoundary(cfd) + elif bc_type == "neumann": + return NeumannBoundary(cfd) + else: + raise ValueError(f"不支持的边界类型:{bc_type}(可选:periodic/dirichlet/neumann)") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05/config.py b/example/1d-linear-convection/weno3/python/05/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05/domain.py b/example/1d-linear-convection/weno3/python/05/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05/eno_weno_comparison.png b/example/1d-linear-convection/weno3/python/05/eno_weno_comparison.png new file mode 100644 index 00000000..8d7135ec Binary files /dev/null and b/example/1d-linear-convection/weno3/python/05/eno_weno_comparison.png differ diff --git a/example/1d-linear-convection/weno3/python/05/flux.py b/example/1d-linear-convection/weno3/python/05/flux.py new file mode 100644 index 00000000..feaa723a --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05/flux.py @@ -0,0 +1,61 @@ +from abc import ABC, abstractmethod + +# ---------------------- 1. 抽象通量计算基类(统一接口) ---------------------- +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass + +# ---------------------- 2. 具体通量计算子类(隔离不同格式) ---------------------- +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + +# ---------------------- 3. 通量计算器工厂(统一创建逻辑) ---------------------- +class FluxCalculatorFactory: + @staticmethod + def create(cfd): + """根据配置创建通量计算器实例""" + flux_type = cfd.config.flux_type + flux_mapping = { + "rusanov": RusanovFluxCalculator, + "engquist-osher": EngquistOsherFluxCalculator, + # 新增通量格式只需加键值对:2: LaxWendroffFluxCalculator + } + if flux_type not in flux_mapping: + raise ValueError(f"不支持的通量类型:{flux_type}(可选:{list(flux_mapping.keys())})") + return flux_mapping[flux_type](cfd) diff --git a/example/1d-linear-convection/weno3/python/05/initial_condition.py b/example/1d-linear-convection/weno3/python/05/initial_condition.py new file mode 100644 index 00000000..166b7dbd --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05/initial_condition.py @@ -0,0 +1,81 @@ +# initial_condition.py +import numpy as np +from abc import ABC, abstractmethod + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现 ---------------------- +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + + +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = self.config.get("domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + + +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = self.config.get("pulse_center", 0.5) + width = self.config.get("pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + + +# ---------------------- 3. 初始条件工厂 ---------------------- +class InitialConditionFactory: + _registry = { + 'step': StepFunctionIC, + 'sin': SineWaveIC, + 'gaussian': GaussianPulseIC, + } + + @classmethod + def create(cls, ic_type, config): + if ic_type not in cls._registry: + raise ValueError(f"未知的初始条件类型: {ic_type}(支持: {list(cls._registry.keys())})") + return cls._registry[ic_type](config) + + @classmethod + def register(cls, name, ic_class): + cls._registry[name] = ic_class \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05/mesh.py b/example/1d-linear-convection/weno3/python/05/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05/plotter.py b/example/1d-linear-convection/weno3/python/05/plotter.py new file mode 100644 index 00000000..9f1a414f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05/plotter.py @@ -0,0 +1,107 @@ +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python/05/reconstructor/__init__.py new file mode 100644 index 00000000..fa17547a --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 可选择性导出具体类(通常不需要) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05/reconstructor/base.py b/example/1d-linear-convection/weno3/python/05/reconstructor/base.py new file mode 100644 index 00000000..3cb4763d --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def reconstruct(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05/reconstructor/eno.py b/example/1d-linear-convection/weno3/python/05/reconstructor/eno.py new file mode 100644 index 00000000..e087fe57 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05/reconstructor/eno.py @@ -0,0 +1,88 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor # 👈 正确导入基类 + + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 3. ENO 重构器 ---------------------- +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def reconstruct(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05/reconstructor/factory.py b/example/1d-linear-convection/weno3/python/05/reconstructor/factory.py new file mode 100644 index 00000000..bf5795bc --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05/reconstructor/factory.py @@ -0,0 +1,35 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor + +class ReconstructorFactory: + _schemes = { + "eno": EnoReconstructor, + "weno3": Weno3Reconstructor, # ← 注意:这里用 "weno3" + # "weno5": Weno5Reconstructor, # 未来扩展 + } + + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + # ✅ 关键:将 "weno" 自动映射为 "weno3", "weno5" 等 + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 检查是否支持 + if scheme not in ReconstructorFactory._schemes: + supported = list(ReconstructorFactory._schemes.keys()) + raise ValueError(f"不支持的重建格式:'{scheme}'(支持:{supported})") + + recon_cls = ReconstructorFactory._schemes[scheme] + + # 根据 scheme 类型创建实例 + if scheme.startswith("eno"): + return recon_cls(order, domain.ntcells) + elif scheme.startswith("weno"): + return recon_cls() # WENO 类通常无参 + # 可扩展 elif... diff --git a/example/1d-linear-convection/weno3/python/05/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python/05/reconstructor/weno3.py new file mode 100644 index 00000000..035c25b6 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05/reconstructor/weno3.py @@ -0,0 +1,56 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor + +class Weno3Reconstructor(Reconstructor): + def reconstruct(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + """使用左偏 stencil (v1,v2,v3) 重建界面值(对应 u_{i+1/2}^-)""" + eps = 1e-6 + beta0 = (v3 - v2)**2 # smoothness indicator for stencil [v2, v3] + beta1 = (v2 - v1)**2 # smoothness indicator for stencil [v1, v2] + + d0, d1 = 2/3, 1/3 # optimal linear weights (for right value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 0.5 * v2 + 0.5 * v3 # reconstruction from [v2, v3] + q1 = -0.5 * v1 + 1.5 * v2 # reconstruction from [v1, v2] + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + """使用右偏 stencil (v1,v2,v3) 重建界面值(对应 u_{i+1/2}^+)""" + eps = 1e-6 + beta0 = (v3 - v2)**2 + beta1 = (v2 - v1)**2 + + d0, d1 = 1/3, 2/3 # optimal linear weights (for left value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 1.5 * v2 - 0.5 * v3 # from [v2, v3] + q1 = 0.5 * v1 + 0.5 * v2 # from [v1, v2] + return w0 * q0 + w1 * q1 diff --git a/example/1d-linear-convection/weno3/python/05/residual.py b/example/1d-linear-convection/weno3/python/05/residual.py new file mode 100644 index 00000000..b4d4d7dc --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._reconstruct() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _reconstruct(self): + """私有方法:界面值重建""" + self.reconstructor.reconstruct(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05/run_eno_weno.py b/example/1d-linear-convection/weno3/python/05/run_eno_weno.py new file mode 100644 index 00000000..fd7f3874 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05/run_eno_weno.py @@ -0,0 +1,52 @@ +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05/solution.py b/example/1d-linear-convection/weno3/python/05/solution.py new file mode 100644 index 00000000..92a46d3f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config.ic_type, config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05/solver.py b/example/1d-linear-convection/weno3/python/05/solver.py new file mode 100644 index 00000000..b2024d2c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05/solver.py @@ -0,0 +1,90 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + +# Flux +from flux import InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory + +# Boundary +from boundary import BoundaryCondition, PeriodicBoundary, DirichletBoundary, NeumannBoundary, BoundaryConditionFactory + +# Time integration +from time_integration import TimeIntegrator, RK1Integrator, RK2Integrator, RK3Integrator, TimeIntegratorFactory + +# Mesh 👈 新增这一行 +from mesh import Mesh + +#from reconstructor import Reconstructor, EnoReconstructor, WenoReconstructor, ReconstructorFactory +from reconstructor import ReconstructorFactory + +from initial_condition import InitialCondition, StepFunctionIC, SineWaveIC, GaussianPulseIC, InitialConditionFactory + +from domain import Domain +from solution import Solution # 👈 新增 + +from config import CfdConfig +from residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config.ic_type, self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + #runge_kutta(self) + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/python/05/time_integration.py b/example/1d-linear-convection/weno3/python/05/time_integration.py new file mode 100644 index 00000000..54dc4277 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05/time_integration.py @@ -0,0 +1,111 @@ +# time_integration.py +from abc import ABC, abstractmethod + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(复用公共逻辑) ---------------------- +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +# ---------------------- 3. 时间推进器工厂(统一创建逻辑) ---------------------- +class TimeIntegratorFactory: + """时间推进器工厂:根据配置创建对应RK实例""" + @staticmethod + def create(cfd): + rk_order = cfd.config.rk_order + integrator_mapping = { + 1: RK1Integrator, + 2: RK2Integrator, + 3: RK3Integrator, + # 新增RK4只需:4: RK4Integrator + } + if rk_order not in integrator_mapping: + raise ValueError(f"不支持的RK阶数:{rk_order}(可选:{list(integrator_mapping.keys())})") + return integrator_mapping[rk_order](cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05a/boundary.py b/example/1d-linear-convection/weno3/python/05a/boundary.py new file mode 100644 index 00000000..ae3fe1d9 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05a/boundary.py @@ -0,0 +1,136 @@ +from abc import ABC, abstractmethod + +# ---------------------- 导入注册系统 ---------------------- +try: + # 首先尝试从新的核心位置导入 + from cfd_core.registry import ComponentRegistry, register_component +except ImportError: + # 如果新的核心模块还不存在,使用我们之前定义的简化版本 + # 这将确保代码立即可用,未来再统一迁移到核心模块 + import sys + import os + # 添加当前目录,以便找到可能在同一文件夹的 registry.py + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + try: + from registry import ComponentRegistry, register_component + print("[boundary] 使用本地 registry.py 中的注册系统") + except ImportError: + # 如果完全找不到,提供一个极简定义防止报错(仅用于过渡) + print("[boundary] 警告: 未找到注册系统,使用极简回退方案") + class ComponentRegistry: + _registries = {} + @classmethod + def register(cls, *args, **kwargs): pass + @classmethod + def create(cls, *args, **kwargs): + # 这是一个非常基本的回退,仅用于演示 + # 在实际替换前,您应确保 registry.py 存在 + raise RuntimeError("注册系统未正确初始化。请确保 registry.py 在 Python 路径中。") + def register_component(*args, **kwargs): + def dummy_decorator(cls): + return cls + return dummy_decorator + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界(进口)固定值(从配置读取) + left_value = self.config.get("left_boundary_value", 1.0) + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # 右边界(出口)固定值(从配置读取) + right_value = self.config.get("right_boundary_value", 2.0) + for ig in range(nghosts): + u[ied + ig] = right_value + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + +# ---------------------- 新的边界条件工厂(使用注册系统) ---------------------- +class BoundaryConditionFactory: + """边界条件工厂:根据配置创建对应边界条件实例(新版本,使用注册系统)""" + + @staticmethod + def create(cfd): + """ + 使用注册系统创建边界条件实例。 + 保持与旧版本完全相同的接口,实现无缝替换。 + """ + # 从配置读取边界类型 + bc_type = cfd.config.boundary_type.lower() + + try: + # 使用注册系统创建实例 + # ComponentRegistry.create(类别, 名称, 传递给构造函数的参数) + bc_instance = ComponentRegistry.create('boundary', bc_type, cfd) + return bc_instance + except (ValueError, RuntimeError) as e: + # 增强错误信息,列出可用的选项 + available = [] + try: + # 尝试从注册表获取可用列表 + all_comps = ComponentRegistry.list_all() + available = all_comps.get('boundary', []) + except: + # 如果注册表不可用,使用硬编码列表作为后备 + available = ['periodic', 'dirichlet', 'neumann'] + + raise ValueError( + f"不支持的边界类型:'{bc_type}'\n" + f"可用类型:{available}\n" + f"原始错误:{e}" + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05a/config.py b/example/1d-linear-convection/weno3/python/05a/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05a/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05a/domain.py b/example/1d-linear-convection/weno3/python/05a/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05a/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05a/flux.py b/example/1d-linear-convection/weno3/python/05a/flux.py new file mode 100644 index 00000000..feaa723a --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05a/flux.py @@ -0,0 +1,61 @@ +from abc import ABC, abstractmethod + +# ---------------------- 1. 抽象通量计算基类(统一接口) ---------------------- +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass + +# ---------------------- 2. 具体通量计算子类(隔离不同格式) ---------------------- +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + +# ---------------------- 3. 通量计算器工厂(统一创建逻辑) ---------------------- +class FluxCalculatorFactory: + @staticmethod + def create(cfd): + """根据配置创建通量计算器实例""" + flux_type = cfd.config.flux_type + flux_mapping = { + "rusanov": RusanovFluxCalculator, + "engquist-osher": EngquistOsherFluxCalculator, + # 新增通量格式只需加键值对:2: LaxWendroffFluxCalculator + } + if flux_type not in flux_mapping: + raise ValueError(f"不支持的通量类型:{flux_type}(可选:{list(flux_mapping.keys())})") + return flux_mapping[flux_type](cfd) diff --git a/example/1d-linear-convection/weno3/python/05a/initial_condition.py b/example/1d-linear-convection/weno3/python/05a/initial_condition.py new file mode 100644 index 00000000..166b7dbd --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05a/initial_condition.py @@ -0,0 +1,81 @@ +# initial_condition.py +import numpy as np +from abc import ABC, abstractmethod + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现 ---------------------- +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + + +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = self.config.get("domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + + +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = self.config.get("pulse_center", 0.5) + width = self.config.get("pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + + +# ---------------------- 3. 初始条件工厂 ---------------------- +class InitialConditionFactory: + _registry = { + 'step': StepFunctionIC, + 'sin': SineWaveIC, + 'gaussian': GaussianPulseIC, + } + + @classmethod + def create(cls, ic_type, config): + if ic_type not in cls._registry: + raise ValueError(f"未知的初始条件类型: {ic_type}(支持: {list(cls._registry.keys())})") + return cls._registry[ic_type](config) + + @classmethod + def register(cls, name, ic_class): + cls._registry[name] = ic_class \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05a/mesh.py b/example/1d-linear-convection/weno3/python/05a/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05a/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05a/plotter.py b/example/1d-linear-convection/weno3/python/05a/plotter.py new file mode 100644 index 00000000..9f1a414f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05a/plotter.py @@ -0,0 +1,107 @@ +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05a/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python/05a/reconstructor/__init__.py new file mode 100644 index 00000000..fa17547a --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05a/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 可选择性导出具体类(通常不需要) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05a/reconstructor/base.py b/example/1d-linear-convection/weno3/python/05a/reconstructor/base.py new file mode 100644 index 00000000..3cb4763d --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05a/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def reconstruct(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05a/reconstructor/eno.py b/example/1d-linear-convection/weno3/python/05a/reconstructor/eno.py new file mode 100644 index 00000000..e087fe57 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05a/reconstructor/eno.py @@ -0,0 +1,88 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor # 👈 正确导入基类 + + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 3. ENO 重构器 ---------------------- +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def reconstruct(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05a/reconstructor/factory.py b/example/1d-linear-convection/weno3/python/05a/reconstructor/factory.py new file mode 100644 index 00000000..bf5795bc --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05a/reconstructor/factory.py @@ -0,0 +1,35 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor + +class ReconstructorFactory: + _schemes = { + "eno": EnoReconstructor, + "weno3": Weno3Reconstructor, # ← 注意:这里用 "weno3" + # "weno5": Weno5Reconstructor, # 未来扩展 + } + + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + # ✅ 关键:将 "weno" 自动映射为 "weno3", "weno5" 等 + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 检查是否支持 + if scheme not in ReconstructorFactory._schemes: + supported = list(ReconstructorFactory._schemes.keys()) + raise ValueError(f"不支持的重建格式:'{scheme}'(支持:{supported})") + + recon_cls = ReconstructorFactory._schemes[scheme] + + # 根据 scheme 类型创建实例 + if scheme.startswith("eno"): + return recon_cls(order, domain.ntcells) + elif scheme.startswith("weno"): + return recon_cls() # WENO 类通常无参 + # 可扩展 elif... diff --git a/example/1d-linear-convection/weno3/python/05a/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python/05a/reconstructor/weno3.py new file mode 100644 index 00000000..cf12db86 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05a/reconstructor/weno3.py @@ -0,0 +1,86 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor + +class Weno3Reconstructor(Reconstructor): + def reconstruct(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + """ + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 # smoothness indicator for stencil [v2, v3] + beta1 = (v2 - v1)**2 # smoothness indicator for stencil [v1, v2] + + d0, d1 = 2/3, 1/3 # optimal linear weights (for right value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 0.5 * v2 + 0.5 * v3 # reconstruction from [v2, v3] + q1 = -0.5 * v1 + 1.5 * v2 # reconstruction from [v1, v2] + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 + beta1 = (v2 - v1)**2 + + d0, d1 = 1/3, 2/3 # optimal linear weights (for left value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 1.5 * v2 - 0.5 * v3 # from [v2, v3] + q1 = 0.5 * v1 + 0.5 * v2 # from [v1, v2] + return w0 * q0 + w1 * q1 + """ diff --git a/example/1d-linear-convection/weno3/python/05a/registry.py b/example/1d-linear-convection/weno3/python/05a/registry.py new file mode 100644 index 00000000..f5bd068a --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05a/registry.py @@ -0,0 +1,56 @@ +""" +CFD组件注册系统核心 +完全独立,不依赖任何现有代码 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表 - 替代硬编码工厂""" + + # 存储所有注册的组件 {类别: {名称: 类}} + _registries: Dict[str, Dict[str, Type]] = {} + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册一个组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + cls._registries[category][name] = component_class + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取已注册的组件类""" + if category not in cls._registries: + raise ValueError(f"❌ 未知类别: {category}") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower().replace('boundary', '').replace('flux', '') + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05a/residual.py b/example/1d-linear-convection/weno3/python/05a/residual.py new file mode 100644 index 00000000..b4d4d7dc --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05a/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._reconstruct() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _reconstruct(self): + """私有方法:界面值重建""" + self.reconstructor.reconstruct(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05a/run_eno_weno.py b/example/1d-linear-convection/weno3/python/05a/run_eno_weno.py new file mode 100644 index 00000000..fd7f3874 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05a/run_eno_weno.py @@ -0,0 +1,52 @@ +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05a/solution.py b/example/1d-linear-convection/weno3/python/05a/solution.py new file mode 100644 index 00000000..92a46d3f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05a/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config.ic_type, config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05a/solver.py b/example/1d-linear-convection/weno3/python/05a/solver.py new file mode 100644 index 00000000..b2024d2c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05a/solver.py @@ -0,0 +1,90 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + +# Flux +from flux import InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory + +# Boundary +from boundary import BoundaryCondition, PeriodicBoundary, DirichletBoundary, NeumannBoundary, BoundaryConditionFactory + +# Time integration +from time_integration import TimeIntegrator, RK1Integrator, RK2Integrator, RK3Integrator, TimeIntegratorFactory + +# Mesh 👈 新增这一行 +from mesh import Mesh + +#from reconstructor import Reconstructor, EnoReconstructor, WenoReconstructor, ReconstructorFactory +from reconstructor import ReconstructorFactory + +from initial_condition import InitialCondition, StepFunctionIC, SineWaveIC, GaussianPulseIC, InitialConditionFactory + +from domain import Domain +from solution import Solution # 👈 新增 + +from config import CfdConfig +from residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config.ic_type, self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + #runge_kutta(self) + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/python/05a/time_integration.py b/example/1d-linear-convection/weno3/python/05a/time_integration.py new file mode 100644 index 00000000..54dc4277 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05a/time_integration.py @@ -0,0 +1,111 @@ +# time_integration.py +from abc import ABC, abstractmethod + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(复用公共逻辑) ---------------------- +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +# ---------------------- 3. 时间推进器工厂(统一创建逻辑) ---------------------- +class TimeIntegratorFactory: + """时间推进器工厂:根据配置创建对应RK实例""" + @staticmethod + def create(cfd): + rk_order = cfd.config.rk_order + integrator_mapping = { + 1: RK1Integrator, + 2: RK2Integrator, + 3: RK3Integrator, + # 新增RK4只需:4: RK4Integrator + } + if rk_order not in integrator_mapping: + raise ValueError(f"不支持的RK阶数:{rk_order}(可选:{list(integrator_mapping.keys())})") + return integrator_mapping[rk_order](cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05b/boundary.py b/example/1d-linear-convection/weno3/python/05b/boundary.py new file mode 100644 index 00000000..ae3fe1d9 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05b/boundary.py @@ -0,0 +1,136 @@ +from abc import ABC, abstractmethod + +# ---------------------- 导入注册系统 ---------------------- +try: + # 首先尝试从新的核心位置导入 + from cfd_core.registry import ComponentRegistry, register_component +except ImportError: + # 如果新的核心模块还不存在,使用我们之前定义的简化版本 + # 这将确保代码立即可用,未来再统一迁移到核心模块 + import sys + import os + # 添加当前目录,以便找到可能在同一文件夹的 registry.py + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + try: + from registry import ComponentRegistry, register_component + print("[boundary] 使用本地 registry.py 中的注册系统") + except ImportError: + # 如果完全找不到,提供一个极简定义防止报错(仅用于过渡) + print("[boundary] 警告: 未找到注册系统,使用极简回退方案") + class ComponentRegistry: + _registries = {} + @classmethod + def register(cls, *args, **kwargs): pass + @classmethod + def create(cls, *args, **kwargs): + # 这是一个非常基本的回退,仅用于演示 + # 在实际替换前,您应确保 registry.py 存在 + raise RuntimeError("注册系统未正确初始化。请确保 registry.py 在 Python 路径中。") + def register_component(*args, **kwargs): + def dummy_decorator(cls): + return cls + return dummy_decorator + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界(进口)固定值(从配置读取) + left_value = self.config.get("left_boundary_value", 1.0) + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # 右边界(出口)固定值(从配置读取) + right_value = self.config.get("right_boundary_value", 2.0) + for ig in range(nghosts): + u[ied + ig] = right_value + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + +# ---------------------- 新的边界条件工厂(使用注册系统) ---------------------- +class BoundaryConditionFactory: + """边界条件工厂:根据配置创建对应边界条件实例(新版本,使用注册系统)""" + + @staticmethod + def create(cfd): + """ + 使用注册系统创建边界条件实例。 + 保持与旧版本完全相同的接口,实现无缝替换。 + """ + # 从配置读取边界类型 + bc_type = cfd.config.boundary_type.lower() + + try: + # 使用注册系统创建实例 + # ComponentRegistry.create(类别, 名称, 传递给构造函数的参数) + bc_instance = ComponentRegistry.create('boundary', bc_type, cfd) + return bc_instance + except (ValueError, RuntimeError) as e: + # 增强错误信息,列出可用的选项 + available = [] + try: + # 尝试从注册表获取可用列表 + all_comps = ComponentRegistry.list_all() + available = all_comps.get('boundary', []) + except: + # 如果注册表不可用,使用硬编码列表作为后备 + available = ['periodic', 'dirichlet', 'neumann'] + + raise ValueError( + f"不支持的边界类型:'{bc_type}'\n" + f"可用类型:{available}\n" + f"原始错误:{e}" + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05b/config.py b/example/1d-linear-convection/weno3/python/05b/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05b/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05b/domain.py b/example/1d-linear-convection/weno3/python/05b/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05b/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05b/flux.py b/example/1d-linear-convection/weno3/python/05b/flux.py new file mode 100644 index 00000000..4ad8a871 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05b/flux.py @@ -0,0 +1,113 @@ +""" +通量计算器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册,替代硬编码工厂 +""" + +from abc import ABC, abstractmethod + +# ---------------------- 导入注册系统 ---------------------- +try: + # 从核心位置或本地导入注册系统 + from cfd_core.registry import ComponentRegistry, register_component +except ImportError: + import sys + import os + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + try: + from registry import ComponentRegistry, register_component + print("[flux] 使用本地 registry.py 中的注册系统") + except ImportError: + # 极简回退方案(实际使用时应该确保registry.py存在) + print("[flux] 警告: 未找到注册系统,使用极简回退方案") + class ComponentRegistry: + _registries = {} + @classmethod + def register(cls, *args, **kwargs): pass + @classmethod + def create(cls, category, name, *args, **kwargs): + # 回退到硬编码逻辑(仅用于过渡) + if category == 'flux' and name == 'rusanov': + return RusanovFluxCalculator(*args, **kwargs) + elif category == 'flux' and name == 'engquist-osher': + return EngquistOsherFluxCalculator(*args, **kwargs) + raise ValueError(f"不支持的 {category}.{name}") + def register_component(*args, **kwargs): + def dummy_decorator(cls): + return cls + return dummy_decorator + +# ---------------------- 1. 抽象通量计算基类(统一接口) ---------------------- +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass + +# ---------------------- 2. 具体通量计算子类(使用装饰器注册) ---------------------- + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + +# ---------------------- 3. 通量计算器工厂(使用注册系统) ---------------------- +class FluxCalculatorFactory: + """通量计算器工厂:根据配置创建对应通量计算器实例""" + @staticmethod + def create(cfd): + """根据配置创建通量计算器实例""" + flux_type = cfd.config.flux_type.lower() + + try: + # 使用注册系统创建实例 + flux_instance = ComponentRegistry.create('flux', flux_type, cfd) + return flux_instance + except (ValueError, RuntimeError) as e: + # 增强错误信息 + available = [] + try: + all_comps = ComponentRegistry.list_all() + available = all_comps.get('flux', []) + except: + # 后备列表 + available = ['rusanov', 'engquist-osher'] + + raise ValueError( + f"不支持的flux类型:'{flux_type}'\n" + f"可用类型:{available}\n" + f"原始错误:{e}" + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05b/initial_condition.py b/example/1d-linear-convection/weno3/python/05b/initial_condition.py new file mode 100644 index 00000000..166b7dbd --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05b/initial_condition.py @@ -0,0 +1,81 @@ +# initial_condition.py +import numpy as np +from abc import ABC, abstractmethod + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现 ---------------------- +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + + +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = self.config.get("domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + + +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = self.config.get("pulse_center", 0.5) + width = self.config.get("pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + + +# ---------------------- 3. 初始条件工厂 ---------------------- +class InitialConditionFactory: + _registry = { + 'step': StepFunctionIC, + 'sin': SineWaveIC, + 'gaussian': GaussianPulseIC, + } + + @classmethod + def create(cls, ic_type, config): + if ic_type not in cls._registry: + raise ValueError(f"未知的初始条件类型: {ic_type}(支持: {list(cls._registry.keys())})") + return cls._registry[ic_type](config) + + @classmethod + def register(cls, name, ic_class): + cls._registry[name] = ic_class \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05b/mesh.py b/example/1d-linear-convection/weno3/python/05b/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05b/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05b/plotter.py b/example/1d-linear-convection/weno3/python/05b/plotter.py new file mode 100644 index 00000000..9f1a414f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05b/plotter.py @@ -0,0 +1,107 @@ +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05b/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python/05b/reconstructor/__init__.py new file mode 100644 index 00000000..fa17547a --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05b/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 可选择性导出具体类(通常不需要) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05b/reconstructor/base.py b/example/1d-linear-convection/weno3/python/05b/reconstructor/base.py new file mode 100644 index 00000000..3cb4763d --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05b/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def reconstruct(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05b/reconstructor/eno.py b/example/1d-linear-convection/weno3/python/05b/reconstructor/eno.py new file mode 100644 index 00000000..e087fe57 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05b/reconstructor/eno.py @@ -0,0 +1,88 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor # 👈 正确导入基类 + + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 3. ENO 重构器 ---------------------- +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def reconstruct(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05b/reconstructor/factory.py b/example/1d-linear-convection/weno3/python/05b/reconstructor/factory.py new file mode 100644 index 00000000..bf5795bc --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05b/reconstructor/factory.py @@ -0,0 +1,35 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor + +class ReconstructorFactory: + _schemes = { + "eno": EnoReconstructor, + "weno3": Weno3Reconstructor, # ← 注意:这里用 "weno3" + # "weno5": Weno5Reconstructor, # 未来扩展 + } + + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + # ✅ 关键:将 "weno" 自动映射为 "weno3", "weno5" 等 + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 检查是否支持 + if scheme not in ReconstructorFactory._schemes: + supported = list(ReconstructorFactory._schemes.keys()) + raise ValueError(f"不支持的重建格式:'{scheme}'(支持:{supported})") + + recon_cls = ReconstructorFactory._schemes[scheme] + + # 根据 scheme 类型创建实例 + if scheme.startswith("eno"): + return recon_cls(order, domain.ntcells) + elif scheme.startswith("weno"): + return recon_cls() # WENO 类通常无参 + # 可扩展 elif... diff --git a/example/1d-linear-convection/weno3/python/05b/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python/05b/reconstructor/weno3.py new file mode 100644 index 00000000..cf12db86 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05b/reconstructor/weno3.py @@ -0,0 +1,86 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor + +class Weno3Reconstructor(Reconstructor): + def reconstruct(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + """ + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 # smoothness indicator for stencil [v2, v3] + beta1 = (v2 - v1)**2 # smoothness indicator for stencil [v1, v2] + + d0, d1 = 2/3, 1/3 # optimal linear weights (for right value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 0.5 * v2 + 0.5 * v3 # reconstruction from [v2, v3] + q1 = -0.5 * v1 + 1.5 * v2 # reconstruction from [v1, v2] + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 + beta1 = (v2 - v1)**2 + + d0, d1 = 1/3, 2/3 # optimal linear weights (for left value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 1.5 * v2 - 0.5 * v3 # from [v2, v3] + q1 = 0.5 * v1 + 0.5 * v2 # from [v1, v2] + return w0 * q0 + w1 * q1 + """ diff --git a/example/1d-linear-convection/weno3/python/05b/registry.py b/example/1d-linear-convection/weno3/python/05b/registry.py new file mode 100644 index 00000000..f5bd068a --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05b/registry.py @@ -0,0 +1,56 @@ +""" +CFD组件注册系统核心 +完全独立,不依赖任何现有代码 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表 - 替代硬编码工厂""" + + # 存储所有注册的组件 {类别: {名称: 类}} + _registries: Dict[str, Dict[str, Type]] = {} + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册一个组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + cls._registries[category][name] = component_class + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取已注册的组件类""" + if category not in cls._registries: + raise ValueError(f"❌ 未知类别: {category}") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower().replace('boundary', '').replace('flux', '') + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05b/residual.py b/example/1d-linear-convection/weno3/python/05b/residual.py new file mode 100644 index 00000000..b4d4d7dc --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05b/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._reconstruct() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _reconstruct(self): + """私有方法:界面值重建""" + self.reconstructor.reconstruct(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05b/run_eno_weno.py b/example/1d-linear-convection/weno3/python/05b/run_eno_weno.py new file mode 100644 index 00000000..fd7f3874 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05b/run_eno_weno.py @@ -0,0 +1,52 @@ +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05b/solution.py b/example/1d-linear-convection/weno3/python/05b/solution.py new file mode 100644 index 00000000..92a46d3f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05b/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config.ic_type, config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05b/solver.py b/example/1d-linear-convection/weno3/python/05b/solver.py new file mode 100644 index 00000000..b2024d2c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05b/solver.py @@ -0,0 +1,90 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + +# Flux +from flux import InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory + +# Boundary +from boundary import BoundaryCondition, PeriodicBoundary, DirichletBoundary, NeumannBoundary, BoundaryConditionFactory + +# Time integration +from time_integration import TimeIntegrator, RK1Integrator, RK2Integrator, RK3Integrator, TimeIntegratorFactory + +# Mesh 👈 新增这一行 +from mesh import Mesh + +#from reconstructor import Reconstructor, EnoReconstructor, WenoReconstructor, ReconstructorFactory +from reconstructor import ReconstructorFactory + +from initial_condition import InitialCondition, StepFunctionIC, SineWaveIC, GaussianPulseIC, InitialConditionFactory + +from domain import Domain +from solution import Solution # 👈 新增 + +from config import CfdConfig +from residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config.ic_type, self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + #runge_kutta(self) + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/python/05b/time_integration.py b/example/1d-linear-convection/weno3/python/05b/time_integration.py new file mode 100644 index 00000000..54dc4277 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05b/time_integration.py @@ -0,0 +1,111 @@ +# time_integration.py +from abc import ABC, abstractmethod + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(复用公共逻辑) ---------------------- +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +# ---------------------- 3. 时间推进器工厂(统一创建逻辑) ---------------------- +class TimeIntegratorFactory: + """时间推进器工厂:根据配置创建对应RK实例""" + @staticmethod + def create(cfd): + rk_order = cfd.config.rk_order + integrator_mapping = { + 1: RK1Integrator, + 2: RK2Integrator, + 3: RK3Integrator, + # 新增RK4只需:4: RK4Integrator + } + if rk_order not in integrator_mapping: + raise ValueError(f"不支持的RK阶数:{rk_order}(可选:{list(integrator_mapping.keys())})") + return integrator_mapping[rk_order](cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05c/boundary.py b/example/1d-linear-convection/weno3/python/05c/boundary.py new file mode 100644 index 00000000..ae3fe1d9 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05c/boundary.py @@ -0,0 +1,136 @@ +from abc import ABC, abstractmethod + +# ---------------------- 导入注册系统 ---------------------- +try: + # 首先尝试从新的核心位置导入 + from cfd_core.registry import ComponentRegistry, register_component +except ImportError: + # 如果新的核心模块还不存在,使用我们之前定义的简化版本 + # 这将确保代码立即可用,未来再统一迁移到核心模块 + import sys + import os + # 添加当前目录,以便找到可能在同一文件夹的 registry.py + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + try: + from registry import ComponentRegistry, register_component + print("[boundary] 使用本地 registry.py 中的注册系统") + except ImportError: + # 如果完全找不到,提供一个极简定义防止报错(仅用于过渡) + print("[boundary] 警告: 未找到注册系统,使用极简回退方案") + class ComponentRegistry: + _registries = {} + @classmethod + def register(cls, *args, **kwargs): pass + @classmethod + def create(cls, *args, **kwargs): + # 这是一个非常基本的回退,仅用于演示 + # 在实际替换前,您应确保 registry.py 存在 + raise RuntimeError("注册系统未正确初始化。请确保 registry.py 在 Python 路径中。") + def register_component(*args, **kwargs): + def dummy_decorator(cls): + return cls + return dummy_decorator + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界(进口)固定值(从配置读取) + left_value = self.config.get("left_boundary_value", 1.0) + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # 右边界(出口)固定值(从配置读取) + right_value = self.config.get("right_boundary_value", 2.0) + for ig in range(nghosts): + u[ied + ig] = right_value + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + +# ---------------------- 新的边界条件工厂(使用注册系统) ---------------------- +class BoundaryConditionFactory: + """边界条件工厂:根据配置创建对应边界条件实例(新版本,使用注册系统)""" + + @staticmethod + def create(cfd): + """ + 使用注册系统创建边界条件实例。 + 保持与旧版本完全相同的接口,实现无缝替换。 + """ + # 从配置读取边界类型 + bc_type = cfd.config.boundary_type.lower() + + try: + # 使用注册系统创建实例 + # ComponentRegistry.create(类别, 名称, 传递给构造函数的参数) + bc_instance = ComponentRegistry.create('boundary', bc_type, cfd) + return bc_instance + except (ValueError, RuntimeError) as e: + # 增强错误信息,列出可用的选项 + available = [] + try: + # 尝试从注册表获取可用列表 + all_comps = ComponentRegistry.list_all() + available = all_comps.get('boundary', []) + except: + # 如果注册表不可用,使用硬编码列表作为后备 + available = ['periodic', 'dirichlet', 'neumann'] + + raise ValueError( + f"不支持的边界类型:'{bc_type}'\n" + f"可用类型:{available}\n" + f"原始错误:{e}" + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05c/config.py b/example/1d-linear-convection/weno3/python/05c/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05c/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05c/domain.py b/example/1d-linear-convection/weno3/python/05c/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05c/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05c/flux.py b/example/1d-linear-convection/weno3/python/05c/flux.py new file mode 100644 index 00000000..4ad8a871 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05c/flux.py @@ -0,0 +1,113 @@ +""" +通量计算器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册,替代硬编码工厂 +""" + +from abc import ABC, abstractmethod + +# ---------------------- 导入注册系统 ---------------------- +try: + # 从核心位置或本地导入注册系统 + from cfd_core.registry import ComponentRegistry, register_component +except ImportError: + import sys + import os + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + try: + from registry import ComponentRegistry, register_component + print("[flux] 使用本地 registry.py 中的注册系统") + except ImportError: + # 极简回退方案(实际使用时应该确保registry.py存在) + print("[flux] 警告: 未找到注册系统,使用极简回退方案") + class ComponentRegistry: + _registries = {} + @classmethod + def register(cls, *args, **kwargs): pass + @classmethod + def create(cls, category, name, *args, **kwargs): + # 回退到硬编码逻辑(仅用于过渡) + if category == 'flux' and name == 'rusanov': + return RusanovFluxCalculator(*args, **kwargs) + elif category == 'flux' and name == 'engquist-osher': + return EngquistOsherFluxCalculator(*args, **kwargs) + raise ValueError(f"不支持的 {category}.{name}") + def register_component(*args, **kwargs): + def dummy_decorator(cls): + return cls + return dummy_decorator + +# ---------------------- 1. 抽象通量计算基类(统一接口) ---------------------- +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass + +# ---------------------- 2. 具体通量计算子类(使用装饰器注册) ---------------------- + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + +# ---------------------- 3. 通量计算器工厂(使用注册系统) ---------------------- +class FluxCalculatorFactory: + """通量计算器工厂:根据配置创建对应通量计算器实例""" + @staticmethod + def create(cfd): + """根据配置创建通量计算器实例""" + flux_type = cfd.config.flux_type.lower() + + try: + # 使用注册系统创建实例 + flux_instance = ComponentRegistry.create('flux', flux_type, cfd) + return flux_instance + except (ValueError, RuntimeError) as e: + # 增强错误信息 + available = [] + try: + all_comps = ComponentRegistry.list_all() + available = all_comps.get('flux', []) + except: + # 后备列表 + available = ['rusanov', 'engquist-osher'] + + raise ValueError( + f"不支持的flux类型:'{flux_type}'\n" + f"可用类型:{available}\n" + f"原始错误:{e}" + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05c/initial_condition.py b/example/1d-linear-convection/weno3/python/05c/initial_condition.py new file mode 100644 index 00000000..fac3be5b --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05c/initial_condition.py @@ -0,0 +1,146 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod + +# ---------------------- 导入注册系统 ---------------------- +try: + from cfd_core.registry import ComponentRegistry, register_component +except ImportError: + import sys + import os + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + try: + from registry import ComponentRegistry, register_component + print("[initial_condition] 使用本地 registry.py 中的注册系统") + except ImportError: + print("[initial_condition] 警告: 未找到注册系统,使用极简回退方案") + class ComponentRegistry: + _registries = {} + @classmethod + def register(cls, *args, **kwargs): pass + @classmethod + def create(cls, category, name, *args, **kwargs): + # 回退到硬编码逻辑 + if category == 'initial_condition': + if name == 'step': + return StepFunctionIC(*args, **kwargs) + elif name == 'sin': + return SineWaveIC(*args, **kwargs) + elif name == 'gaussian': + return GaussianPulseIC(*args, **kwargs) + raise ValueError(f"不支持的 {category}.{name}") + def register_component(*args, **kwargs): + def dummy_decorator(cls): + return cls + return dummy_decorator + + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = self.config.get("domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = self.config.get("pulse_center", 0.5) + width = self.config.get("pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + + +# ---------------------- 3. 初始条件工厂(使用注册系统) ---------------------- +class InitialConditionFactory: + """初始条件工厂:根据配置创建对应初始条件实例""" + + @classmethod + def create(cls, ic_type, config): + """创建初始条件实例""" + ic_type_lower = ic_type.lower() + + try: + # 使用注册系统创建实例 + ic_instance = ComponentRegistry.create('initial_condition', ic_type_lower, config) + return ic_instance + except (ValueError, RuntimeError) as e: + # 增强错误信息 + available = [] + try: + all_comps = ComponentRegistry.list_all() + available = all_comps.get('initial_condition', []) + except: + # 后备列表 + available = ['step', 'sin', 'gaussian'] + + raise ValueError( + f"未知的初始条件类型: '{ic_type}'\n" + f"支持的类型: {available}\n" + f"原始错误: {e}" + ) + + # 保持原有的注册方法,用于向后兼容或动态注册 + _registry = {} # 旧式注册表,可能被其他代码使用 + + @classmethod + def register(cls, name, ic_class): + """注册初始条件类(兼容旧接口)""" + cls._registry[name] = ic_class + # 同时注册到新系统(如果可能) + try: + ComponentRegistry.register('initial_condition', name, ic_class) + except: + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05c/mesh.py b/example/1d-linear-convection/weno3/python/05c/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05c/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05c/plotter.py b/example/1d-linear-convection/weno3/python/05c/plotter.py new file mode 100644 index 00000000..9f1a414f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05c/plotter.py @@ -0,0 +1,107 @@ +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05c/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python/05c/reconstructor/__init__.py new file mode 100644 index 00000000..fa17547a --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05c/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 可选择性导出具体类(通常不需要) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05c/reconstructor/base.py b/example/1d-linear-convection/weno3/python/05c/reconstructor/base.py new file mode 100644 index 00000000..3cb4763d --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05c/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def reconstruct(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05c/reconstructor/eno.py b/example/1d-linear-convection/weno3/python/05c/reconstructor/eno.py new file mode 100644 index 00000000..e087fe57 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05c/reconstructor/eno.py @@ -0,0 +1,88 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor # 👈 正确导入基类 + + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 3. ENO 重构器 ---------------------- +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def reconstruct(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05c/reconstructor/factory.py b/example/1d-linear-convection/weno3/python/05c/reconstructor/factory.py new file mode 100644 index 00000000..bf5795bc --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05c/reconstructor/factory.py @@ -0,0 +1,35 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor + +class ReconstructorFactory: + _schemes = { + "eno": EnoReconstructor, + "weno3": Weno3Reconstructor, # ← 注意:这里用 "weno3" + # "weno5": Weno5Reconstructor, # 未来扩展 + } + + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + # ✅ 关键:将 "weno" 自动映射为 "weno3", "weno5" 等 + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 检查是否支持 + if scheme not in ReconstructorFactory._schemes: + supported = list(ReconstructorFactory._schemes.keys()) + raise ValueError(f"不支持的重建格式:'{scheme}'(支持:{supported})") + + recon_cls = ReconstructorFactory._schemes[scheme] + + # 根据 scheme 类型创建实例 + if scheme.startswith("eno"): + return recon_cls(order, domain.ntcells) + elif scheme.startswith("weno"): + return recon_cls() # WENO 类通常无参 + # 可扩展 elif... diff --git a/example/1d-linear-convection/weno3/python/05c/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python/05c/reconstructor/weno3.py new file mode 100644 index 00000000..cf12db86 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05c/reconstructor/weno3.py @@ -0,0 +1,86 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor + +class Weno3Reconstructor(Reconstructor): + def reconstruct(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + """ + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 # smoothness indicator for stencil [v2, v3] + beta1 = (v2 - v1)**2 # smoothness indicator for stencil [v1, v2] + + d0, d1 = 2/3, 1/3 # optimal linear weights (for right value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 0.5 * v2 + 0.5 * v3 # reconstruction from [v2, v3] + q1 = -0.5 * v1 + 1.5 * v2 # reconstruction from [v1, v2] + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 + beta1 = (v2 - v1)**2 + + d0, d1 = 1/3, 2/3 # optimal linear weights (for left value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 1.5 * v2 - 0.5 * v3 # from [v2, v3] + q1 = 0.5 * v1 + 0.5 * v2 # from [v1, v2] + return w0 * q0 + w1 * q1 + """ diff --git a/example/1d-linear-convection/weno3/python/05c/registry.py b/example/1d-linear-convection/weno3/python/05c/registry.py new file mode 100644 index 00000000..f5bd068a --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05c/registry.py @@ -0,0 +1,56 @@ +""" +CFD组件注册系统核心 +完全独立,不依赖任何现有代码 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表 - 替代硬编码工厂""" + + # 存储所有注册的组件 {类别: {名称: 类}} + _registries: Dict[str, Dict[str, Type]] = {} + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册一个组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + cls._registries[category][name] = component_class + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取已注册的组件类""" + if category not in cls._registries: + raise ValueError(f"❌ 未知类别: {category}") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower().replace('boundary', '').replace('flux', '') + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05c/residual.py b/example/1d-linear-convection/weno3/python/05c/residual.py new file mode 100644 index 00000000..b4d4d7dc --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05c/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._reconstruct() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _reconstruct(self): + """私有方法:界面值重建""" + self.reconstructor.reconstruct(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05c/run_eno_weno.py b/example/1d-linear-convection/weno3/python/05c/run_eno_weno.py new file mode 100644 index 00000000..fd7f3874 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05c/run_eno_weno.py @@ -0,0 +1,52 @@ +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05c/solution.py b/example/1d-linear-convection/weno3/python/05c/solution.py new file mode 100644 index 00000000..92a46d3f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05c/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config.ic_type, config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05c/solver.py b/example/1d-linear-convection/weno3/python/05c/solver.py new file mode 100644 index 00000000..b2024d2c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05c/solver.py @@ -0,0 +1,90 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + +# Flux +from flux import InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory + +# Boundary +from boundary import BoundaryCondition, PeriodicBoundary, DirichletBoundary, NeumannBoundary, BoundaryConditionFactory + +# Time integration +from time_integration import TimeIntegrator, RK1Integrator, RK2Integrator, RK3Integrator, TimeIntegratorFactory + +# Mesh 👈 新增这一行 +from mesh import Mesh + +#from reconstructor import Reconstructor, EnoReconstructor, WenoReconstructor, ReconstructorFactory +from reconstructor import ReconstructorFactory + +from initial_condition import InitialCondition, StepFunctionIC, SineWaveIC, GaussianPulseIC, InitialConditionFactory + +from domain import Domain +from solution import Solution # 👈 新增 + +from config import CfdConfig +from residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config.ic_type, self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + #runge_kutta(self) + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/python/05c/time_integration.py b/example/1d-linear-convection/weno3/python/05c/time_integration.py new file mode 100644 index 00000000..54dc4277 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05c/time_integration.py @@ -0,0 +1,111 @@ +# time_integration.py +from abc import ABC, abstractmethod + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(复用公共逻辑) ---------------------- +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +# ---------------------- 3. 时间推进器工厂(统一创建逻辑) ---------------------- +class TimeIntegratorFactory: + """时间推进器工厂:根据配置创建对应RK实例""" + @staticmethod + def create(cfd): + rk_order = cfd.config.rk_order + integrator_mapping = { + 1: RK1Integrator, + 2: RK2Integrator, + 3: RK3Integrator, + # 新增RK4只需:4: RK4Integrator + } + if rk_order not in integrator_mapping: + raise ValueError(f"不支持的RK阶数:{rk_order}(可选:{list(integrator_mapping.keys())})") + return integrator_mapping[rk_order](cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05d/boundary.py b/example/1d-linear-convection/weno3/python/05d/boundary.py new file mode 100644 index 00000000..ae3fe1d9 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05d/boundary.py @@ -0,0 +1,136 @@ +from abc import ABC, abstractmethod + +# ---------------------- 导入注册系统 ---------------------- +try: + # 首先尝试从新的核心位置导入 + from cfd_core.registry import ComponentRegistry, register_component +except ImportError: + # 如果新的核心模块还不存在,使用我们之前定义的简化版本 + # 这将确保代码立即可用,未来再统一迁移到核心模块 + import sys + import os + # 添加当前目录,以便找到可能在同一文件夹的 registry.py + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + try: + from registry import ComponentRegistry, register_component + print("[boundary] 使用本地 registry.py 中的注册系统") + except ImportError: + # 如果完全找不到,提供一个极简定义防止报错(仅用于过渡) + print("[boundary] 警告: 未找到注册系统,使用极简回退方案") + class ComponentRegistry: + _registries = {} + @classmethod + def register(cls, *args, **kwargs): pass + @classmethod + def create(cls, *args, **kwargs): + # 这是一个非常基本的回退,仅用于演示 + # 在实际替换前,您应确保 registry.py 存在 + raise RuntimeError("注册系统未正确初始化。请确保 registry.py 在 Python 路径中。") + def register_component(*args, **kwargs): + def dummy_decorator(cls): + return cls + return dummy_decorator + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界(进口)固定值(从配置读取) + left_value = self.config.get("left_boundary_value", 1.0) + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # 右边界(出口)固定值(从配置读取) + right_value = self.config.get("right_boundary_value", 2.0) + for ig in range(nghosts): + u[ied + ig] = right_value + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + +# ---------------------- 新的边界条件工厂(使用注册系统) ---------------------- +class BoundaryConditionFactory: + """边界条件工厂:根据配置创建对应边界条件实例(新版本,使用注册系统)""" + + @staticmethod + def create(cfd): + """ + 使用注册系统创建边界条件实例。 + 保持与旧版本完全相同的接口,实现无缝替换。 + """ + # 从配置读取边界类型 + bc_type = cfd.config.boundary_type.lower() + + try: + # 使用注册系统创建实例 + # ComponentRegistry.create(类别, 名称, 传递给构造函数的参数) + bc_instance = ComponentRegistry.create('boundary', bc_type, cfd) + return bc_instance + except (ValueError, RuntimeError) as e: + # 增强错误信息,列出可用的选项 + available = [] + try: + # 尝试从注册表获取可用列表 + all_comps = ComponentRegistry.list_all() + available = all_comps.get('boundary', []) + except: + # 如果注册表不可用,使用硬编码列表作为后备 + available = ['periodic', 'dirichlet', 'neumann'] + + raise ValueError( + f"不支持的边界类型:'{bc_type}'\n" + f"可用类型:{available}\n" + f"原始错误:{e}" + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05d/config.py b/example/1d-linear-convection/weno3/python/05d/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05d/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05d/domain.py b/example/1d-linear-convection/weno3/python/05d/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05d/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05d/flux.py b/example/1d-linear-convection/weno3/python/05d/flux.py new file mode 100644 index 00000000..4ad8a871 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05d/flux.py @@ -0,0 +1,113 @@ +""" +通量计算器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册,替代硬编码工厂 +""" + +from abc import ABC, abstractmethod + +# ---------------------- 导入注册系统 ---------------------- +try: + # 从核心位置或本地导入注册系统 + from cfd_core.registry import ComponentRegistry, register_component +except ImportError: + import sys + import os + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + try: + from registry import ComponentRegistry, register_component + print("[flux] 使用本地 registry.py 中的注册系统") + except ImportError: + # 极简回退方案(实际使用时应该确保registry.py存在) + print("[flux] 警告: 未找到注册系统,使用极简回退方案") + class ComponentRegistry: + _registries = {} + @classmethod + def register(cls, *args, **kwargs): pass + @classmethod + def create(cls, category, name, *args, **kwargs): + # 回退到硬编码逻辑(仅用于过渡) + if category == 'flux' and name == 'rusanov': + return RusanovFluxCalculator(*args, **kwargs) + elif category == 'flux' and name == 'engquist-osher': + return EngquistOsherFluxCalculator(*args, **kwargs) + raise ValueError(f"不支持的 {category}.{name}") + def register_component(*args, **kwargs): + def dummy_decorator(cls): + return cls + return dummy_decorator + +# ---------------------- 1. 抽象通量计算基类(统一接口) ---------------------- +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass + +# ---------------------- 2. 具体通量计算子类(使用装饰器注册) ---------------------- + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + +# ---------------------- 3. 通量计算器工厂(使用注册系统) ---------------------- +class FluxCalculatorFactory: + """通量计算器工厂:根据配置创建对应通量计算器实例""" + @staticmethod + def create(cfd): + """根据配置创建通量计算器实例""" + flux_type = cfd.config.flux_type.lower() + + try: + # 使用注册系统创建实例 + flux_instance = ComponentRegistry.create('flux', flux_type, cfd) + return flux_instance + except (ValueError, RuntimeError) as e: + # 增强错误信息 + available = [] + try: + all_comps = ComponentRegistry.list_all() + available = all_comps.get('flux', []) + except: + # 后备列表 + available = ['rusanov', 'engquist-osher'] + + raise ValueError( + f"不支持的flux类型:'{flux_type}'\n" + f"可用类型:{available}\n" + f"原始错误:{e}" + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05d/initial_condition.py b/example/1d-linear-convection/weno3/python/05d/initial_condition.py new file mode 100644 index 00000000..fac3be5b --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05d/initial_condition.py @@ -0,0 +1,146 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod + +# ---------------------- 导入注册系统 ---------------------- +try: + from cfd_core.registry import ComponentRegistry, register_component +except ImportError: + import sys + import os + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + try: + from registry import ComponentRegistry, register_component + print("[initial_condition] 使用本地 registry.py 中的注册系统") + except ImportError: + print("[initial_condition] 警告: 未找到注册系统,使用极简回退方案") + class ComponentRegistry: + _registries = {} + @classmethod + def register(cls, *args, **kwargs): pass + @classmethod + def create(cls, category, name, *args, **kwargs): + # 回退到硬编码逻辑 + if category == 'initial_condition': + if name == 'step': + return StepFunctionIC(*args, **kwargs) + elif name == 'sin': + return SineWaveIC(*args, **kwargs) + elif name == 'gaussian': + return GaussianPulseIC(*args, **kwargs) + raise ValueError(f"不支持的 {category}.{name}") + def register_component(*args, **kwargs): + def dummy_decorator(cls): + return cls + return dummy_decorator + + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = self.config.get("domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = self.config.get("pulse_center", 0.5) + width = self.config.get("pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + + +# ---------------------- 3. 初始条件工厂(使用注册系统) ---------------------- +class InitialConditionFactory: + """初始条件工厂:根据配置创建对应初始条件实例""" + + @classmethod + def create(cls, ic_type, config): + """创建初始条件实例""" + ic_type_lower = ic_type.lower() + + try: + # 使用注册系统创建实例 + ic_instance = ComponentRegistry.create('initial_condition', ic_type_lower, config) + return ic_instance + except (ValueError, RuntimeError) as e: + # 增强错误信息 + available = [] + try: + all_comps = ComponentRegistry.list_all() + available = all_comps.get('initial_condition', []) + except: + # 后备列表 + available = ['step', 'sin', 'gaussian'] + + raise ValueError( + f"未知的初始条件类型: '{ic_type}'\n" + f"支持的类型: {available}\n" + f"原始错误: {e}" + ) + + # 保持原有的注册方法,用于向后兼容或动态注册 + _registry = {} # 旧式注册表,可能被其他代码使用 + + @classmethod + def register(cls, name, ic_class): + """注册初始条件类(兼容旧接口)""" + cls._registry[name] = ic_class + # 同时注册到新系统(如果可能) + try: + ComponentRegistry.register('initial_condition', name, ic_class) + except: + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05d/mesh.py b/example/1d-linear-convection/weno3/python/05d/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05d/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05d/plotter.py b/example/1d-linear-convection/weno3/python/05d/plotter.py new file mode 100644 index 00000000..9f1a414f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05d/plotter.py @@ -0,0 +1,107 @@ +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05d/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python/05d/reconstructor/__init__.py new file mode 100644 index 00000000..fa17547a --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05d/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 可选择性导出具体类(通常不需要) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05d/reconstructor/base.py b/example/1d-linear-convection/weno3/python/05d/reconstructor/base.py new file mode 100644 index 00000000..3cb4763d --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05d/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def reconstruct(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05d/reconstructor/eno.py b/example/1d-linear-convection/weno3/python/05d/reconstructor/eno.py new file mode 100644 index 00000000..e087fe57 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05d/reconstructor/eno.py @@ -0,0 +1,88 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor # 👈 正确导入基类 + + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 3. ENO 重构器 ---------------------- +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def reconstruct(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05d/reconstructor/factory.py b/example/1d-linear-convection/weno3/python/05d/reconstructor/factory.py new file mode 100644 index 00000000..bf5795bc --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05d/reconstructor/factory.py @@ -0,0 +1,35 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor + +class ReconstructorFactory: + _schemes = { + "eno": EnoReconstructor, + "weno3": Weno3Reconstructor, # ← 注意:这里用 "weno3" + # "weno5": Weno5Reconstructor, # 未来扩展 + } + + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + # ✅ 关键:将 "weno" 自动映射为 "weno3", "weno5" 等 + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 检查是否支持 + if scheme not in ReconstructorFactory._schemes: + supported = list(ReconstructorFactory._schemes.keys()) + raise ValueError(f"不支持的重建格式:'{scheme}'(支持:{supported})") + + recon_cls = ReconstructorFactory._schemes[scheme] + + # 根据 scheme 类型创建实例 + if scheme.startswith("eno"): + return recon_cls(order, domain.ntcells) + elif scheme.startswith("weno"): + return recon_cls() # WENO 类通常无参 + # 可扩展 elif... diff --git a/example/1d-linear-convection/weno3/python/05d/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python/05d/reconstructor/weno3.py new file mode 100644 index 00000000..cf12db86 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05d/reconstructor/weno3.py @@ -0,0 +1,86 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor + +class Weno3Reconstructor(Reconstructor): + def reconstruct(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + """ + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 # smoothness indicator for stencil [v2, v3] + beta1 = (v2 - v1)**2 # smoothness indicator for stencil [v1, v2] + + d0, d1 = 2/3, 1/3 # optimal linear weights (for right value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 0.5 * v2 + 0.5 * v3 # reconstruction from [v2, v3] + q1 = -0.5 * v1 + 1.5 * v2 # reconstruction from [v1, v2] + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 + beta1 = (v2 - v1)**2 + + d0, d1 = 1/3, 2/3 # optimal linear weights (for left value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 1.5 * v2 - 0.5 * v3 # from [v2, v3] + q1 = 0.5 * v1 + 0.5 * v2 # from [v1, v2] + return w0 * q0 + w1 * q1 + """ diff --git a/example/1d-linear-convection/weno3/python/05d/registry.py b/example/1d-linear-convection/weno3/python/05d/registry.py new file mode 100644 index 00000000..f5bd068a --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05d/registry.py @@ -0,0 +1,56 @@ +""" +CFD组件注册系统核心 +完全独立,不依赖任何现有代码 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表 - 替代硬编码工厂""" + + # 存储所有注册的组件 {类别: {名称: 类}} + _registries: Dict[str, Dict[str, Type]] = {} + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册一个组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + cls._registries[category][name] = component_class + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取已注册的组件类""" + if category not in cls._registries: + raise ValueError(f"❌ 未知类别: {category}") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower().replace('boundary', '').replace('flux', '') + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05d/residual.py b/example/1d-linear-convection/weno3/python/05d/residual.py new file mode 100644 index 00000000..b4d4d7dc --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05d/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._reconstruct() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _reconstruct(self): + """私有方法:界面值重建""" + self.reconstructor.reconstruct(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05d/run_eno_weno.py b/example/1d-linear-convection/weno3/python/05d/run_eno_weno.py new file mode 100644 index 00000000..fd7f3874 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05d/run_eno_weno.py @@ -0,0 +1,52 @@ +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05d/solution.py b/example/1d-linear-convection/weno3/python/05d/solution.py new file mode 100644 index 00000000..92a46d3f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05d/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config.ic_type, config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05d/solver.py b/example/1d-linear-convection/weno3/python/05d/solver.py new file mode 100644 index 00000000..b2024d2c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05d/solver.py @@ -0,0 +1,90 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + +# Flux +from flux import InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory + +# Boundary +from boundary import BoundaryCondition, PeriodicBoundary, DirichletBoundary, NeumannBoundary, BoundaryConditionFactory + +# Time integration +from time_integration import TimeIntegrator, RK1Integrator, RK2Integrator, RK3Integrator, TimeIntegratorFactory + +# Mesh 👈 新增这一行 +from mesh import Mesh + +#from reconstructor import Reconstructor, EnoReconstructor, WenoReconstructor, ReconstructorFactory +from reconstructor import ReconstructorFactory + +from initial_condition import InitialCondition, StepFunctionIC, SineWaveIC, GaussianPulseIC, InitialConditionFactory + +from domain import Domain +from solution import Solution # 👈 新增 + +from config import CfdConfig +from residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config.ic_type, self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + #runge_kutta(self) + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/python/05d/time_integration.py b/example/1d-linear-convection/weno3/python/05d/time_integration.py new file mode 100644 index 00000000..af2320cc --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05d/time_integration.py @@ -0,0 +1,173 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod + +# ---------------------- 导入注册系统 ---------------------- +try: + from cfd_core.registry import ComponentRegistry, register_component +except ImportError: + import sys + import os + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + try: + from registry import ComponentRegistry, register_component + print("[time_integration] 使用本地 registry.py 中的注册系统") + except ImportError: + print("[time_integration] 警告: 未找到注册系统,使用极简回退方案") + class ComponentRegistry: + _registries = {} + @classmethod + def register(cls, *args, **kwargs): pass + @classmethod + def create(cls, category, name, *args, **kwargs): + # 回退到硬编码逻辑 + if category == 'integrator': + if name == 'rk1': + return RK1Integrator(*args, **kwargs) + elif name == 'rk2': + return RK2Integrator(*args, **kwargs) + elif name == 'rk3': + return RK3Integrator(*args, **kwargs) + raise ValueError(f"不支持的 {category}.{name}") + def register_component(*args, **kwargs): + def dummy_decorator(cls): + return cls + return dummy_decorator + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +# ---------------------- 3. 时间推进器工厂(使用注册系统) ---------------------- +class TimeIntegratorFactory: + """时间推进器工厂:根据配置创建对应RK实例""" + + @staticmethod + def create(cfd): + """根据配置创建时间推进器实例""" + rk_order = cfd.config.rk_order + + # 将数字顺序映射为注册系统使用的名字 + # 注意:注册时使用了 'rk1', 'rk2', 'rk3' 这样的名字 + integrator_name = f'rk{rk_order}' + + try: + # 使用注册系统创建实例 + integrator_instance = ComponentRegistry.create('integrator', integrator_name, cfd) + return integrator_instance + except (ValueError, RuntimeError) as e: + # 增强错误信息 + available = [] + try: + all_comps = ComponentRegistry.list_all() + available = all_comps.get('integrator', []) + except: + # 后备列表 + available = ['rk1', 'rk2', 'rk3'] + + # 尝试提供更友好的错误信息 + available_orders = [int(name[2:]) for name in available if name.startswith('rk')] + + raise ValueError( + f"不支持的RK阶数:{rk_order}\n" + f"支持的RK阶数:{available_orders}\n" + f"原始错误:{e}" + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05e/boundary.py b/example/1d-linear-convection/weno3/python/05e/boundary.py new file mode 100644 index 00000000..ae3fe1d9 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05e/boundary.py @@ -0,0 +1,136 @@ +from abc import ABC, abstractmethod + +# ---------------------- 导入注册系统 ---------------------- +try: + # 首先尝试从新的核心位置导入 + from cfd_core.registry import ComponentRegistry, register_component +except ImportError: + # 如果新的核心模块还不存在,使用我们之前定义的简化版本 + # 这将确保代码立即可用,未来再统一迁移到核心模块 + import sys + import os + # 添加当前目录,以便找到可能在同一文件夹的 registry.py + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + try: + from registry import ComponentRegistry, register_component + print("[boundary] 使用本地 registry.py 中的注册系统") + except ImportError: + # 如果完全找不到,提供一个极简定义防止报错(仅用于过渡) + print("[boundary] 警告: 未找到注册系统,使用极简回退方案") + class ComponentRegistry: + _registries = {} + @classmethod + def register(cls, *args, **kwargs): pass + @classmethod + def create(cls, *args, **kwargs): + # 这是一个非常基本的回退,仅用于演示 + # 在实际替换前,您应确保 registry.py 存在 + raise RuntimeError("注册系统未正确初始化。请确保 registry.py 在 Python 路径中。") + def register_component(*args, **kwargs): + def dummy_decorator(cls): + return cls + return dummy_decorator + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界(进口)固定值(从配置读取) + left_value = self.config.get("left_boundary_value", 1.0) + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # 右边界(出口)固定值(从配置读取) + right_value = self.config.get("right_boundary_value", 2.0) + for ig in range(nghosts): + u[ied + ig] = right_value + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + +# ---------------------- 新的边界条件工厂(使用注册系统) ---------------------- +class BoundaryConditionFactory: + """边界条件工厂:根据配置创建对应边界条件实例(新版本,使用注册系统)""" + + @staticmethod + def create(cfd): + """ + 使用注册系统创建边界条件实例。 + 保持与旧版本完全相同的接口,实现无缝替换。 + """ + # 从配置读取边界类型 + bc_type = cfd.config.boundary_type.lower() + + try: + # 使用注册系统创建实例 + # ComponentRegistry.create(类别, 名称, 传递给构造函数的参数) + bc_instance = ComponentRegistry.create('boundary', bc_type, cfd) + return bc_instance + except (ValueError, RuntimeError) as e: + # 增强错误信息,列出可用的选项 + available = [] + try: + # 尝试从注册表获取可用列表 + all_comps = ComponentRegistry.list_all() + available = all_comps.get('boundary', []) + except: + # 如果注册表不可用,使用硬编码列表作为后备 + available = ['periodic', 'dirichlet', 'neumann'] + + raise ValueError( + f"不支持的边界类型:'{bc_type}'\n" + f"可用类型:{available}\n" + f"原始错误:{e}" + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05e/config.py b/example/1d-linear-convection/weno3/python/05e/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05e/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05e/domain.py b/example/1d-linear-convection/weno3/python/05e/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05e/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05e/flux.py b/example/1d-linear-convection/weno3/python/05e/flux.py new file mode 100644 index 00000000..4ad8a871 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05e/flux.py @@ -0,0 +1,113 @@ +""" +通量计算器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册,替代硬编码工厂 +""" + +from abc import ABC, abstractmethod + +# ---------------------- 导入注册系统 ---------------------- +try: + # 从核心位置或本地导入注册系统 + from cfd_core.registry import ComponentRegistry, register_component +except ImportError: + import sys + import os + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + try: + from registry import ComponentRegistry, register_component + print("[flux] 使用本地 registry.py 中的注册系统") + except ImportError: + # 极简回退方案(实际使用时应该确保registry.py存在) + print("[flux] 警告: 未找到注册系统,使用极简回退方案") + class ComponentRegistry: + _registries = {} + @classmethod + def register(cls, *args, **kwargs): pass + @classmethod + def create(cls, category, name, *args, **kwargs): + # 回退到硬编码逻辑(仅用于过渡) + if category == 'flux' and name == 'rusanov': + return RusanovFluxCalculator(*args, **kwargs) + elif category == 'flux' and name == 'engquist-osher': + return EngquistOsherFluxCalculator(*args, **kwargs) + raise ValueError(f"不支持的 {category}.{name}") + def register_component(*args, **kwargs): + def dummy_decorator(cls): + return cls + return dummy_decorator + +# ---------------------- 1. 抽象通量计算基类(统一接口) ---------------------- +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass + +# ---------------------- 2. 具体通量计算子类(使用装饰器注册) ---------------------- + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + +# ---------------------- 3. 通量计算器工厂(使用注册系统) ---------------------- +class FluxCalculatorFactory: + """通量计算器工厂:根据配置创建对应通量计算器实例""" + @staticmethod + def create(cfd): + """根据配置创建通量计算器实例""" + flux_type = cfd.config.flux_type.lower() + + try: + # 使用注册系统创建实例 + flux_instance = ComponentRegistry.create('flux', flux_type, cfd) + return flux_instance + except (ValueError, RuntimeError) as e: + # 增强错误信息 + available = [] + try: + all_comps = ComponentRegistry.list_all() + available = all_comps.get('flux', []) + except: + # 后备列表 + available = ['rusanov', 'engquist-osher'] + + raise ValueError( + f"不支持的flux类型:'{flux_type}'\n" + f"可用类型:{available}\n" + f"原始错误:{e}" + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05e/initial_condition.py b/example/1d-linear-convection/weno3/python/05e/initial_condition.py new file mode 100644 index 00000000..fac3be5b --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05e/initial_condition.py @@ -0,0 +1,146 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod + +# ---------------------- 导入注册系统 ---------------------- +try: + from cfd_core.registry import ComponentRegistry, register_component +except ImportError: + import sys + import os + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + try: + from registry import ComponentRegistry, register_component + print("[initial_condition] 使用本地 registry.py 中的注册系统") + except ImportError: + print("[initial_condition] 警告: 未找到注册系统,使用极简回退方案") + class ComponentRegistry: + _registries = {} + @classmethod + def register(cls, *args, **kwargs): pass + @classmethod + def create(cls, category, name, *args, **kwargs): + # 回退到硬编码逻辑 + if category == 'initial_condition': + if name == 'step': + return StepFunctionIC(*args, **kwargs) + elif name == 'sin': + return SineWaveIC(*args, **kwargs) + elif name == 'gaussian': + return GaussianPulseIC(*args, **kwargs) + raise ValueError(f"不支持的 {category}.{name}") + def register_component(*args, **kwargs): + def dummy_decorator(cls): + return cls + return dummy_decorator + + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = self.config.get("domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = self.config.get("pulse_center", 0.5) + width = self.config.get("pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + + +# ---------------------- 3. 初始条件工厂(使用注册系统) ---------------------- +class InitialConditionFactory: + """初始条件工厂:根据配置创建对应初始条件实例""" + + @classmethod + def create(cls, ic_type, config): + """创建初始条件实例""" + ic_type_lower = ic_type.lower() + + try: + # 使用注册系统创建实例 + ic_instance = ComponentRegistry.create('initial_condition', ic_type_lower, config) + return ic_instance + except (ValueError, RuntimeError) as e: + # 增强错误信息 + available = [] + try: + all_comps = ComponentRegistry.list_all() + available = all_comps.get('initial_condition', []) + except: + # 后备列表 + available = ['step', 'sin', 'gaussian'] + + raise ValueError( + f"未知的初始条件类型: '{ic_type}'\n" + f"支持的类型: {available}\n" + f"原始错误: {e}" + ) + + # 保持原有的注册方法,用于向后兼容或动态注册 + _registry = {} # 旧式注册表,可能被其他代码使用 + + @classmethod + def register(cls, name, ic_class): + """注册初始条件类(兼容旧接口)""" + cls._registry[name] = ic_class + # 同时注册到新系统(如果可能) + try: + ComponentRegistry.register('initial_condition', name, ic_class) + except: + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05e/mesh.py b/example/1d-linear-convection/weno3/python/05e/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05e/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05e/plotter.py b/example/1d-linear-convection/weno3/python/05e/plotter.py new file mode 100644 index 00000000..9f1a414f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05e/plotter.py @@ -0,0 +1,107 @@ +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05e/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python/05e/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05e/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05e/reconstructor/base.py b/example/1d-linear-convection/weno3/python/05e/reconstructor/base.py new file mode 100644 index 00000000..3cb4763d --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05e/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def reconstruct(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05e/reconstructor/eno.py b/example/1d-linear-convection/weno3/python/05e/reconstructor/eno.py new file mode 100644 index 00000000..74e0feae --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05e/reconstructor/eno.py @@ -0,0 +1,108 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor + +# 导入注册系统 +try: + from cfd_core.registry import ComponentRegistry, register_component +except ImportError: + import sys + import os + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + try: + from registry import ComponentRegistry, register_component + print("[reconstructor.eno] 使用本地 registry.py 中的注册系统") + except ImportError: + print("[reconstructor.eno] 警告: 未找到注册系统") + def register_component(*args, **kwargs): + def dummy_decorator(cls): + return cls + return dummy_decorator + + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def reconstruct(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05e/reconstructor/factory.py b/example/1d-linear-convection/weno3/python/05e/reconstructor/factory.py new file mode 100644 index 00000000..3c39ec3f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05e/reconstructor/factory.py @@ -0,0 +1,66 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor + +# 导入注册系统 +try: + from cfd_core.registry import ComponentRegistry +except ImportError: + import sys + import os + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + try: + from registry import ComponentRegistry + print("[reconstructor.factory] 使用本地 registry.py 中的注册系统") + except ImportError: + print("[reconstructor.factory] 警告: 未找到注册系统") + # 提供一个回退方案 + class ComponentRegistry: + @staticmethod + def create(category, name, *args, **kwargs): + if category == 'reconstructor': + if name == 'eno': + return EnoReconstructor(*args, **kwargs) + elif name == 'weno3': + return Weno3Reconstructor(*args, **kwargs) + raise ValueError(f"不支持的 {category}.{name}") + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + print(f"[ReconstructorFactory] 请求创建重建器: {scheme}") + + try: + # 根据具体类型传递参数 + if scheme == "eno": + if order is None: + order = 3 + # ENO 需要 order 和 ntcells + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # ✅ 关键修复:WENO3 不需要参数! + return ComponentRegistry.create('reconstructor', scheme) # 没有参数! + else: + # 其他情况默认传递 config + return ComponentRegistry.create('reconstructor', scheme, config) + + except Exception as e: + print(f"注册系统失败,使用硬编码: {e}") + # 硬编码回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + return Weno3Reconstructor() # ✅ 无参数! + else: + raise ValueError(f"不支持的重建格式:{scheme}") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05e/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python/05e/reconstructor/weno3.py new file mode 100644 index 00000000..8aa67ee1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05e/reconstructor/weno3.py @@ -0,0 +1,105 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor + +# 导入注册系统 +try: + from cfd_core.registry import ComponentRegistry, register_component +except ImportError: + import sys + import os + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + try: + from registry import ComponentRegistry, register_component + print("[reconstructor.weno3] 使用本地 registry.py 中的注册系统") + except ImportError: + print("[reconstructor.weno3] 警告: 未找到注册系统") + def register_component(*args, **kwargs): + def dummy_decorator(cls): + return cls + return dummy_decorator + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def reconstruct(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + """ + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 # smoothness indicator for stencil [v2, v3] + beta1 = (v2 - v1)**2 # smoothness indicator for stencil [v1, v2] + + d0, d1 = 2/3, 1/3 # optimal linear weights (for right value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 0.5 * v2 + 0.5 * v3 # reconstruction from [v2, v3] + q1 = -0.5 * v1 + 1.5 * v2 # reconstruction from [v1, v2] + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 + beta1 = (v2 - v1)**2 + + d0, d1 = 1/3, 2/3 # optimal linear weights (for left value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 1.5 * v2 - 0.5 * v3 # from [v2, v3] + q1 = 0.5 * v1 + 0.5 * v2 # from [v1, v2] + return w0 * q0 + w1 * q1 + """ diff --git a/example/1d-linear-convection/weno3/python/05e/registry.py b/example/1d-linear-convection/weno3/python/05e/registry.py new file mode 100644 index 00000000..f5bd068a --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05e/registry.py @@ -0,0 +1,56 @@ +""" +CFD组件注册系统核心 +完全独立,不依赖任何现有代码 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表 - 替代硬编码工厂""" + + # 存储所有注册的组件 {类别: {名称: 类}} + _registries: Dict[str, Dict[str, Type]] = {} + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册一个组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + cls._registries[category][name] = component_class + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取已注册的组件类""" + if category not in cls._registries: + raise ValueError(f"❌ 未知类别: {category}") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower().replace('boundary', '').replace('flux', '') + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05e/residual.py b/example/1d-linear-convection/weno3/python/05e/residual.py new file mode 100644 index 00000000..b4d4d7dc --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05e/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._reconstruct() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _reconstruct(self): + """私有方法:界面值重建""" + self.reconstructor.reconstruct(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05e/run_eno_weno.py b/example/1d-linear-convection/weno3/python/05e/run_eno_weno.py new file mode 100644 index 00000000..fd7f3874 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05e/run_eno_weno.py @@ -0,0 +1,52 @@ +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05e/solution.py b/example/1d-linear-convection/weno3/python/05e/solution.py new file mode 100644 index 00000000..92a46d3f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05e/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config.ic_type, config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05e/solver.py b/example/1d-linear-convection/weno3/python/05e/solver.py new file mode 100644 index 00000000..b2024d2c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05e/solver.py @@ -0,0 +1,90 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + +# Flux +from flux import InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory + +# Boundary +from boundary import BoundaryCondition, PeriodicBoundary, DirichletBoundary, NeumannBoundary, BoundaryConditionFactory + +# Time integration +from time_integration import TimeIntegrator, RK1Integrator, RK2Integrator, RK3Integrator, TimeIntegratorFactory + +# Mesh 👈 新增这一行 +from mesh import Mesh + +#from reconstructor import Reconstructor, EnoReconstructor, WenoReconstructor, ReconstructorFactory +from reconstructor import ReconstructorFactory + +from initial_condition import InitialCondition, StepFunctionIC, SineWaveIC, GaussianPulseIC, InitialConditionFactory + +from domain import Domain +from solution import Solution # 👈 新增 + +from config import CfdConfig +from residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config.ic_type, self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + #runge_kutta(self) + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/python/05e/time_integration.py b/example/1d-linear-convection/weno3/python/05e/time_integration.py new file mode 100644 index 00000000..af2320cc --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05e/time_integration.py @@ -0,0 +1,173 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod + +# ---------------------- 导入注册系统 ---------------------- +try: + from cfd_core.registry import ComponentRegistry, register_component +except ImportError: + import sys + import os + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + try: + from registry import ComponentRegistry, register_component + print("[time_integration] 使用本地 registry.py 中的注册系统") + except ImportError: + print("[time_integration] 警告: 未找到注册系统,使用极简回退方案") + class ComponentRegistry: + _registries = {} + @classmethod + def register(cls, *args, **kwargs): pass + @classmethod + def create(cls, category, name, *args, **kwargs): + # 回退到硬编码逻辑 + if category == 'integrator': + if name == 'rk1': + return RK1Integrator(*args, **kwargs) + elif name == 'rk2': + return RK2Integrator(*args, **kwargs) + elif name == 'rk3': + return RK3Integrator(*args, **kwargs) + raise ValueError(f"不支持的 {category}.{name}") + def register_component(*args, **kwargs): + def dummy_decorator(cls): + return cls + return dummy_decorator + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +# ---------------------- 3. 时间推进器工厂(使用注册系统) ---------------------- +class TimeIntegratorFactory: + """时间推进器工厂:根据配置创建对应RK实例""" + + @staticmethod + def create(cfd): + """根据配置创建时间推进器实例""" + rk_order = cfd.config.rk_order + + # 将数字顺序映射为注册系统使用的名字 + # 注意:注册时使用了 'rk1', 'rk2', 'rk3' 这样的名字 + integrator_name = f'rk{rk_order}' + + try: + # 使用注册系统创建实例 + integrator_instance = ComponentRegistry.create('integrator', integrator_name, cfd) + return integrator_instance + except (ValueError, RuntimeError) as e: + # 增强错误信息 + available = [] + try: + all_comps = ComponentRegistry.list_all() + available = all_comps.get('integrator', []) + except: + # 后备列表 + available = ['rk1', 'rk2', 'rk3'] + + # 尝试提供更友好的错误信息 + available_orders = [int(name[2:]) for name in available if name.startswith('rk')] + + raise ValueError( + f"不支持的RK阶数:{rk_order}\n" + f"支持的RK阶数:{available_orders}\n" + f"原始错误:{e}" + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05f/boundary.py b/example/1d-linear-convection/weno3/python/05f/boundary.py new file mode 100644 index 00000000..7bfacf2a --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05f/boundary.py @@ -0,0 +1,151 @@ +from abc import ABC, abstractmethod + +# ---------------------- 导入注册系统 ---------------------- +try: + # 首先尝试从新的核心位置导入 + from cfd_core.registry import ComponentRegistry, register_component +except ImportError: + # 如果新的核心模块还不存在,使用我们之前定义的简化版本 + # 这将确保代码立即可用,未来再统一迁移到核心模块 + import sys + import os + # 添加当前目录,以便找到可能在同一文件夹的 registry.py + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + try: + from registry import ComponentRegistry, register_component + print("[boundary] 使用本地 registry.py 中的注册系统") + except ImportError: + # 如果完全找不到,提供一个极简定义防止报错(仅用于过渡) + print("[boundary] 警告: 未找到注册系统,使用极简回退方案") + class ComponentRegistry: + _registries = {} + @classmethod + def register(cls, *args, **kwargs): pass + @classmethod + def create(cls, *args, **kwargs): + # 这是一个非常基本的回退,仅用于演示 + # 在实际替换前,您应确保 registry.py 存在 + raise RuntimeError("注册系统未正确初始化。请确保 registry.py 在 Python 路径中。") + def register_component(*args, **kwargs): + def dummy_decorator(cls): + return cls + return dummy_decorator + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +# ---------------------- 新的边界条件工厂(使用注册系统) ---------------------- +class BoundaryConditionFactory: + """边界条件工厂:根据配置创建对应边界条件实例(新版本,使用注册系统)""" + + @staticmethod + def create(cfd): + """ + 使用注册系统创建边界条件实例。 + 保持与旧版本完全相同的接口,实现无缝替换。 + """ + # 从配置读取边界类型 + bc_type = cfd.config.boundary_type.lower() + + try: + # 使用注册系统创建实例 + # ComponentRegistry.create(类别, 名称, 传递给构造函数的参数) + bc_instance = ComponentRegistry.create('boundary', bc_type, cfd) + return bc_instance + except (ValueError, RuntimeError) as e: + # 增强错误信息,列出可用的选项 + available = [] + try: + # 尝试从注册表获取可用列表 + all_comps = ComponentRegistry.list_all() + available = all_comps.get('boundary', []) + except: + # 如果注册表不可用,使用硬编码列表作为后备 + available = ['periodic', 'dirichlet', 'neumann'] + + raise ValueError( + f"不支持的边界类型:'{bc_type}'\n" + f"可用类型:{available}\n" + f"原始错误:{e}" + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05f/config.py b/example/1d-linear-convection/weno3/python/05f/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05f/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05f/domain.py b/example/1d-linear-convection/weno3/python/05f/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05f/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05f/flux.py b/example/1d-linear-convection/weno3/python/05f/flux.py new file mode 100644 index 00000000..4ad8a871 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05f/flux.py @@ -0,0 +1,113 @@ +""" +通量计算器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册,替代硬编码工厂 +""" + +from abc import ABC, abstractmethod + +# ---------------------- 导入注册系统 ---------------------- +try: + # 从核心位置或本地导入注册系统 + from cfd_core.registry import ComponentRegistry, register_component +except ImportError: + import sys + import os + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + try: + from registry import ComponentRegistry, register_component + print("[flux] 使用本地 registry.py 中的注册系统") + except ImportError: + # 极简回退方案(实际使用时应该确保registry.py存在) + print("[flux] 警告: 未找到注册系统,使用极简回退方案") + class ComponentRegistry: + _registries = {} + @classmethod + def register(cls, *args, **kwargs): pass + @classmethod + def create(cls, category, name, *args, **kwargs): + # 回退到硬编码逻辑(仅用于过渡) + if category == 'flux' and name == 'rusanov': + return RusanovFluxCalculator(*args, **kwargs) + elif category == 'flux' and name == 'engquist-osher': + return EngquistOsherFluxCalculator(*args, **kwargs) + raise ValueError(f"不支持的 {category}.{name}") + def register_component(*args, **kwargs): + def dummy_decorator(cls): + return cls + return dummy_decorator + +# ---------------------- 1. 抽象通量计算基类(统一接口) ---------------------- +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass + +# ---------------------- 2. 具体通量计算子类(使用装饰器注册) ---------------------- + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + +# ---------------------- 3. 通量计算器工厂(使用注册系统) ---------------------- +class FluxCalculatorFactory: + """通量计算器工厂:根据配置创建对应通量计算器实例""" + @staticmethod + def create(cfd): + """根据配置创建通量计算器实例""" + flux_type = cfd.config.flux_type.lower() + + try: + # 使用注册系统创建实例 + flux_instance = ComponentRegistry.create('flux', flux_type, cfd) + return flux_instance + except (ValueError, RuntimeError) as e: + # 增强错误信息 + available = [] + try: + all_comps = ComponentRegistry.list_all() + available = all_comps.get('flux', []) + except: + # 后备列表 + available = ['rusanov', 'engquist-osher'] + + raise ValueError( + f"不支持的flux类型:'{flux_type}'\n" + f"可用类型:{available}\n" + f"原始错误:{e}" + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05f/initial_condition.py b/example/1d-linear-convection/weno3/python/05f/initial_condition.py new file mode 100644 index 00000000..fac3be5b --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05f/initial_condition.py @@ -0,0 +1,146 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod + +# ---------------------- 导入注册系统 ---------------------- +try: + from cfd_core.registry import ComponentRegistry, register_component +except ImportError: + import sys + import os + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + try: + from registry import ComponentRegistry, register_component + print("[initial_condition] 使用本地 registry.py 中的注册系统") + except ImportError: + print("[initial_condition] 警告: 未找到注册系统,使用极简回退方案") + class ComponentRegistry: + _registries = {} + @classmethod + def register(cls, *args, **kwargs): pass + @classmethod + def create(cls, category, name, *args, **kwargs): + # 回退到硬编码逻辑 + if category == 'initial_condition': + if name == 'step': + return StepFunctionIC(*args, **kwargs) + elif name == 'sin': + return SineWaveIC(*args, **kwargs) + elif name == 'gaussian': + return GaussianPulseIC(*args, **kwargs) + raise ValueError(f"不支持的 {category}.{name}") + def register_component(*args, **kwargs): + def dummy_decorator(cls): + return cls + return dummy_decorator + + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = self.config.get("domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = self.config.get("pulse_center", 0.5) + width = self.config.get("pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + + +# ---------------------- 3. 初始条件工厂(使用注册系统) ---------------------- +class InitialConditionFactory: + """初始条件工厂:根据配置创建对应初始条件实例""" + + @classmethod + def create(cls, ic_type, config): + """创建初始条件实例""" + ic_type_lower = ic_type.lower() + + try: + # 使用注册系统创建实例 + ic_instance = ComponentRegistry.create('initial_condition', ic_type_lower, config) + return ic_instance + except (ValueError, RuntimeError) as e: + # 增强错误信息 + available = [] + try: + all_comps = ComponentRegistry.list_all() + available = all_comps.get('initial_condition', []) + except: + # 后备列表 + available = ['step', 'sin', 'gaussian'] + + raise ValueError( + f"未知的初始条件类型: '{ic_type}'\n" + f"支持的类型: {available}\n" + f"原始错误: {e}" + ) + + # 保持原有的注册方法,用于向后兼容或动态注册 + _registry = {} # 旧式注册表,可能被其他代码使用 + + @classmethod + def register(cls, name, ic_class): + """注册初始条件类(兼容旧接口)""" + cls._registry[name] = ic_class + # 同时注册到新系统(如果可能) + try: + ComponentRegistry.register('initial_condition', name, ic_class) + except: + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05f/mesh.py b/example/1d-linear-convection/weno3/python/05f/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05f/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05f/plotter.py b/example/1d-linear-convection/weno3/python/05f/plotter.py new file mode 100644 index 00000000..9f1a414f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05f/plotter.py @@ -0,0 +1,107 @@ +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05f/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python/05f/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05f/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05f/reconstructor/base.py b/example/1d-linear-convection/weno3/python/05f/reconstructor/base.py new file mode 100644 index 00000000..3cb4763d --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05f/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def reconstruct(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05f/reconstructor/eno.py b/example/1d-linear-convection/weno3/python/05f/reconstructor/eno.py new file mode 100644 index 00000000..74e0feae --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05f/reconstructor/eno.py @@ -0,0 +1,108 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor + +# 导入注册系统 +try: + from cfd_core.registry import ComponentRegistry, register_component +except ImportError: + import sys + import os + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + try: + from registry import ComponentRegistry, register_component + print("[reconstructor.eno] 使用本地 registry.py 中的注册系统") + except ImportError: + print("[reconstructor.eno] 警告: 未找到注册系统") + def register_component(*args, **kwargs): + def dummy_decorator(cls): + return cls + return dummy_decorator + + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def reconstruct(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05f/reconstructor/factory.py b/example/1d-linear-convection/weno3/python/05f/reconstructor/factory.py new file mode 100644 index 00000000..3c39ec3f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05f/reconstructor/factory.py @@ -0,0 +1,66 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor + +# 导入注册系统 +try: + from cfd_core.registry import ComponentRegistry +except ImportError: + import sys + import os + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + try: + from registry import ComponentRegistry + print("[reconstructor.factory] 使用本地 registry.py 中的注册系统") + except ImportError: + print("[reconstructor.factory] 警告: 未找到注册系统") + # 提供一个回退方案 + class ComponentRegistry: + @staticmethod + def create(category, name, *args, **kwargs): + if category == 'reconstructor': + if name == 'eno': + return EnoReconstructor(*args, **kwargs) + elif name == 'weno3': + return Weno3Reconstructor(*args, **kwargs) + raise ValueError(f"不支持的 {category}.{name}") + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + print(f"[ReconstructorFactory] 请求创建重建器: {scheme}") + + try: + # 根据具体类型传递参数 + if scheme == "eno": + if order is None: + order = 3 + # ENO 需要 order 和 ntcells + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # ✅ 关键修复:WENO3 不需要参数! + return ComponentRegistry.create('reconstructor', scheme) # 没有参数! + else: + # 其他情况默认传递 config + return ComponentRegistry.create('reconstructor', scheme, config) + + except Exception as e: + print(f"注册系统失败,使用硬编码: {e}") + # 硬编码回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + return Weno3Reconstructor() # ✅ 无参数! + else: + raise ValueError(f"不支持的重建格式:{scheme}") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05f/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python/05f/reconstructor/weno3.py new file mode 100644 index 00000000..8aa67ee1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05f/reconstructor/weno3.py @@ -0,0 +1,105 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor + +# 导入注册系统 +try: + from cfd_core.registry import ComponentRegistry, register_component +except ImportError: + import sys + import os + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + try: + from registry import ComponentRegistry, register_component + print("[reconstructor.weno3] 使用本地 registry.py 中的注册系统") + except ImportError: + print("[reconstructor.weno3] 警告: 未找到注册系统") + def register_component(*args, **kwargs): + def dummy_decorator(cls): + return cls + return dummy_decorator + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def reconstruct(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + """ + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 # smoothness indicator for stencil [v2, v3] + beta1 = (v2 - v1)**2 # smoothness indicator for stencil [v1, v2] + + d0, d1 = 2/3, 1/3 # optimal linear weights (for right value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 0.5 * v2 + 0.5 * v3 # reconstruction from [v2, v3] + q1 = -0.5 * v1 + 1.5 * v2 # reconstruction from [v1, v2] + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 + beta1 = (v2 - v1)**2 + + d0, d1 = 1/3, 2/3 # optimal linear weights (for left value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 1.5 * v2 - 0.5 * v3 # from [v2, v3] + q1 = 0.5 * v1 + 0.5 * v2 # from [v1, v2] + return w0 * q0 + w1 * q1 + """ diff --git a/example/1d-linear-convection/weno3/python/05f/registry.py b/example/1d-linear-convection/weno3/python/05f/registry.py new file mode 100644 index 00000000..f5bd068a --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05f/registry.py @@ -0,0 +1,56 @@ +""" +CFD组件注册系统核心 +完全独立,不依赖任何现有代码 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表 - 替代硬编码工厂""" + + # 存储所有注册的组件 {类别: {名称: 类}} + _registries: Dict[str, Dict[str, Type]] = {} + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册一个组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + cls._registries[category][name] = component_class + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取已注册的组件类""" + if category not in cls._registries: + raise ValueError(f"❌ 未知类别: {category}") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower().replace('boundary', '').replace('flux', '') + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05f/residual.py b/example/1d-linear-convection/weno3/python/05f/residual.py new file mode 100644 index 00000000..b4d4d7dc --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05f/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._reconstruct() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _reconstruct(self): + """私有方法:界面值重建""" + self.reconstructor.reconstruct(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05f/run_eno_weno.py b/example/1d-linear-convection/weno3/python/05f/run_eno_weno.py new file mode 100644 index 00000000..fd7f3874 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05f/run_eno_weno.py @@ -0,0 +1,52 @@ +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05f/solution.py b/example/1d-linear-convection/weno3/python/05f/solution.py new file mode 100644 index 00000000..92a46d3f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05f/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config.ic_type, config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05f/solver.py b/example/1d-linear-convection/weno3/python/05f/solver.py new file mode 100644 index 00000000..b2024d2c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05f/solver.py @@ -0,0 +1,90 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + +# Flux +from flux import InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory + +# Boundary +from boundary import BoundaryCondition, PeriodicBoundary, DirichletBoundary, NeumannBoundary, BoundaryConditionFactory + +# Time integration +from time_integration import TimeIntegrator, RK1Integrator, RK2Integrator, RK3Integrator, TimeIntegratorFactory + +# Mesh 👈 新增这一行 +from mesh import Mesh + +#from reconstructor import Reconstructor, EnoReconstructor, WenoReconstructor, ReconstructorFactory +from reconstructor import ReconstructorFactory + +from initial_condition import InitialCondition, StepFunctionIC, SineWaveIC, GaussianPulseIC, InitialConditionFactory + +from domain import Domain +from solution import Solution # 👈 新增 + +from config import CfdConfig +from residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config.ic_type, self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + #runge_kutta(self) + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/python/05f/test_all_boundaries.py b/example/1d-linear-convection/weno3/python/05f/test_all_boundaries.py new file mode 100644 index 00000000..68b0f2fd --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05f/test_all_boundaries.py @@ -0,0 +1,68 @@ +# test_all_boundaries_fixed.py +import numpy as np +from config import CfdConfig +from mesh import Mesh +from domain import Domain +from solution import Solution +from boundary import BoundaryConditionFactory + +print("=== 测试所有边界条件 (修复版) ===") + +config = CfdConfig() +mesh = Mesh() +domain = Domain(config, mesh) +solution = Solution(config, domain) + +# 测试每种边界条件 +boundary_types = ['periodic', 'dirichlet', 'neumann'] + +for bc_type in boundary_types: + print(f"\n测试 {bc_type.upper()} 边界:") + + # 创建新的配置对象,避免属性污染 + config = CfdConfig() + config.boundary_type = bc_type + + # ✅ 确保设置正确的属性(不是字典键) + if bc_type == 'dirichlet': + config.left_boundary_value = 1.0 # 直接设置属性 + config.right_boundary_value = 2.0 # 直接设置属性 + + mesh = Mesh() + domain = Domain(config, mesh) + + # 创建模拟CFD对象 + class MockCFD: + def __init__(self, config, domain): + self.config = config + self.domain = domain + + cfd = MockCFD(config, domain) + + # 创建边界条件 + try: + bc = BoundaryConditionFactory.create(cfd) + print(f" ✅ 创建: {type(bc).__name__}") + + # 应用边界条件 + u = np.ones(domain.ntcells) * 5.0 + bc.apply(u) + print(f" ✅ 应用成功") + + # 简单验证 + if bc_type == 'periodic': + print(f" 左ghost层: {u[:domain.nghosts]}") + print(f" 右ghost层: {u[-domain.nghosts:]}") + elif bc_type == 'dirichlet': + print(f" 左边界值: {u[:domain.nghosts]} (应接近1.0)") + print(f" 右边界值: {u[-domain.nghosts:]} (应接近2.0)") + elif bc_type == 'neumann': + print(f" 左边界梯度: {u[domain.nghosts-1]} = {u[domain.nghosts]}") + print(f" 右边界梯度: {u[-domain.nghosts]} = {u[-domain.nghosts-1]}") + + except Exception as e: + print(f" ❌ 失败: {type(e).__name__}: {e}") + import traceback + traceback.print_exc() + +print("\n=== 测试完成 ===") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05f/test_registry_complete.py b/example/1d-linear-convection/weno3/python/05f/test_registry_complete.py new file mode 100644 index 00000000..16bdd211 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05f/test_registry_complete.py @@ -0,0 +1,75 @@ +# test_registry_complete.py +import sys +import os +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +print("=== 完整的注册系统测试 ===") + +# 导入所有模块以触发注册 +print("\n1. 导入所有CFD模块(触发注册):") +modules = [ + 'boundary', + 'flux', + 'initial_condition', + 'time_integration', + # reconstructor 已经在导入时注册 +] + +for module in modules: + try: + __import__(module) + print(f" ✅ {module}") + except ImportError as e: + print(f" ❌ {module}: {e}") + +# 检查注册表 +print("\n2. 检查注册表内容:") + +from registry import ComponentRegistry + +all_components = ComponentRegistry.list_all() +total_components = 0 + +for category, components in all_components.items(): + print(f"\n {category.upper()} ({len(components)}种):") + for name in sorted(components): + try: + cls = ComponentRegistry.get(category, name) + print(f" - {name}: {cls.__name__}") + total_components += 1 + except: + print(f" - {name}: <无法获取类>") + +print(f"\n总计: {total_components} 个组件已注册") +print("\n3. 测试创建每个类别的组件:") + +# 测试数据 +class MockConfig: + boundary_type = 'periodic' + flux_type = 'rusanov' + recon_scheme = 'eno' + rk_order = 2 + ic_type = 'step' + spatial_order = 3 + +class MockCFD: + def __init__(self): + self.config = MockConfig() + self.domain = type('Domain', (), {'ntcells': 50, 'mesh': type('Mesh', (), {'nnodes': 10})()})() + +# 测试每个类别 +test_cases = [ + ('boundary', 'periodic', MockCFD()), + ('flux', 'rusanov', MockCFD()), + ('initial_condition', 'step', MockConfig()), + ('integrator', 'rk2', MockCFD()), +] + +for category, name, arg in test_cases: + try: + instance = ComponentRegistry.create(category, name, arg) + print(f" ✅ {category}.{name}: 创建成功 -> {type(instance).__name__}") + except Exception as e: + print(f" ❌ {category}.{name}: 创建失败 -> {type(e).__name__}: {e}") + +print("\n✅ 注册系统测试完成!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/05f/time_integration.py b/example/1d-linear-convection/weno3/python/05f/time_integration.py new file mode 100644 index 00000000..af2320cc --- /dev/null +++ b/example/1d-linear-convection/weno3/python/05f/time_integration.py @@ -0,0 +1,173 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod + +# ---------------------- 导入注册系统 ---------------------- +try: + from cfd_core.registry import ComponentRegistry, register_component +except ImportError: + import sys + import os + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + try: + from registry import ComponentRegistry, register_component + print("[time_integration] 使用本地 registry.py 中的注册系统") + except ImportError: + print("[time_integration] 警告: 未找到注册系统,使用极简回退方案") + class ComponentRegistry: + _registries = {} + @classmethod + def register(cls, *args, **kwargs): pass + @classmethod + def create(cls, category, name, *args, **kwargs): + # 回退到硬编码逻辑 + if category == 'integrator': + if name == 'rk1': + return RK1Integrator(*args, **kwargs) + elif name == 'rk2': + return RK2Integrator(*args, **kwargs) + elif name == 'rk3': + return RK3Integrator(*args, **kwargs) + raise ValueError(f"不支持的 {category}.{name}") + def register_component(*args, **kwargs): + def dummy_decorator(cls): + return cls + return dummy_decorator + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +# ---------------------- 3. 时间推进器工厂(使用注册系统) ---------------------- +class TimeIntegratorFactory: + """时间推进器工厂:根据配置创建对应RK实例""" + + @staticmethod + def create(cfd): + """根据配置创建时间推进器实例""" + rk_order = cfd.config.rk_order + + # 将数字顺序映射为注册系统使用的名字 + # 注意:注册时使用了 'rk1', 'rk2', 'rk3' 这样的名字 + integrator_name = f'rk{rk_order}' + + try: + # 使用注册系统创建实例 + integrator_instance = ComponentRegistry.create('integrator', integrator_name, cfd) + return integrator_instance + except (ValueError, RuntimeError) as e: + # 增强错误信息 + available = [] + try: + all_comps = ComponentRegistry.list_all() + available = all_comps.get('integrator', []) + except: + # 后备列表 + available = ['rk1', 'rk2', 'rk3'] + + # 尝试提供更友好的错误信息 + available_orders = [int(name[2:]) for name in available if name.startswith('rk')] + + raise ValueError( + f"不支持的RK阶数:{rk_order}\n" + f"支持的RK阶数:{available_orders}\n" + f"原始错误:{e}" + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06/boundary.py b/example/1d-linear-convection/weno3/python/06/boundary.py new file mode 100644 index 00000000..6054f92d --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06/boundary.py @@ -0,0 +1,103 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06/cfd_registry.py b/example/1d-linear-convection/weno3/python/06/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06/config.py b/example/1d-linear-convection/weno3/python/06/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06/domain.py b/example/1d-linear-convection/weno3/python/06/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06/factories/base_factory.py b/example/1d-linear-convection/weno3/python/06/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06/flux.py b/example/1d-linear-convection/weno3/python/06/flux.py new file mode 100644 index 00000000..beb9ed48 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06/flux.py @@ -0,0 +1,73 @@ +""" +通量计算器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册,替代硬编码工厂 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象通量计算基类(统一接口) ---------------------- +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass + +# ---------------------- 2. 具体通量计算子类(使用装饰器注册) ---------------------- + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + +class FluxCalculatorFactory: + """通量计算器工厂""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD对象 + + Returns: + 通量计算器实例 + """ + from factories.base_factory import BaseFactory + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06/initial_condition.py b/example/1d-linear-convection/weno3/python/06/initial_condition.py new file mode 100644 index 00000000..7c568c70 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06/initial_condition.py @@ -0,0 +1,90 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = self.config.get("domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = self.config.get("pulse_center", 0.5) + width = self.config.get("pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, ic_type: str, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', ic_type, config) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06/mesh.py b/example/1d-linear-convection/weno3/python/06/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06/plotter.py b/example/1d-linear-convection/weno3/python/06/plotter.py new file mode 100644 index 00000000..9f1a414f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06/plotter.py @@ -0,0 +1,107 @@ +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python/06/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06/reconstructor/base.py b/example/1d-linear-convection/weno3/python/06/reconstructor/base.py new file mode 100644 index 00000000..3cb4763d --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def reconstruct(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06/reconstructor/eno.py b/example/1d-linear-convection/weno3/python/06/reconstructor/eno.py new file mode 100644 index 00000000..c2fb385d --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def reconstruct(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06/reconstructor/factory.py b/example/1d-linear-convection/weno3/python/06/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python/06/reconstructor/weno3.py new file mode 100644 index 00000000..6e8c3f23 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06/reconstructor/weno3.py @@ -0,0 +1,88 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def reconstruct(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + """ + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 # smoothness indicator for stencil [v2, v3] + beta1 = (v2 - v1)**2 # smoothness indicator for stencil [v1, v2] + + d0, d1 = 2/3, 1/3 # optimal linear weights (for right value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 0.5 * v2 + 0.5 * v3 # reconstruction from [v2, v3] + q1 = -0.5 * v1 + 1.5 * v2 # reconstruction from [v1, v2] + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 + beta1 = (v2 - v1)**2 + + d0, d1 = 1/3, 2/3 # optimal linear weights (for left value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 1.5 * v2 - 0.5 * v3 # from [v2, v3] + q1 = 0.5 * v1 + 0.5 * v2 # from [v1, v2] + return w0 * q0 + w1 * q1 + """ diff --git a/example/1d-linear-convection/weno3/python/06/registry.py b/example/1d-linear-convection/weno3/python/06/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06/residual.py b/example/1d-linear-convection/weno3/python/06/residual.py new file mode 100644 index 00000000..b4d4d7dc --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._reconstruct() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _reconstruct(self): + """私有方法:界面值重建""" + self.reconstructor.reconstruct(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06/run_eno_weno.py b/example/1d-linear-convection/weno3/python/06/run_eno_weno.py new file mode 100644 index 00000000..fd7f3874 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06/run_eno_weno.py @@ -0,0 +1,52 @@ +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06/solution.py b/example/1d-linear-convection/weno3/python/06/solution.py new file mode 100644 index 00000000..92a46d3f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config.ic_type, config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06/solver.py b/example/1d-linear-convection/weno3/python/06/solver.py new file mode 100644 index 00000000..0d0b442d --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06/solver.py @@ -0,0 +1,86 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + + +from flux import InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory + +from boundary import BoundaryCondition, PeriodicBoundary, DirichletBoundary, NeumannBoundary, BoundaryConditionFactory + +from time_integration import TimeIntegrator, RK1Integrator, RK2Integrator, RK3Integrator, TimeIntegratorFactory + +from mesh import Mesh + +from reconstructor import ReconstructorFactory + +from initial_condition import InitialCondition, StepFunctionIC, SineWaveIC, GaussianPulseIC, InitialConditionFactory + +from domain import Domain +from solution import Solution + +from config import CfdConfig +from residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config.ic_type, self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + #runge_kutta(self) + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/python/06/test_simplified_imports.py b/example/1d-linear-convection/weno3/python/06/test_simplified_imports.py new file mode 100644 index 00000000..fccce5f2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06/test_simplified_imports.py @@ -0,0 +1,45 @@ +# test_simplified_imports.py +""" +测试简化后的导入系统 +""" + +print("=== 测试简化后的导入系统 ===") + +# 测试导入所有模块 +modules = [ + 'boundary', + 'flux', + 'initial_condition', + 'time_integration', +] + +print("\n1. 导入所有模块:") +for module in modules: + try: + __import__(module) + print(f" ✅ {module}") + except ImportError as e: + print(f" ❌ {module}: {e}") + +# 测试注册系统 +print("\n2. 测试注册系统:") +from cfd_registry import ComponentRegistry + +print("所有注册的组件:") +all_components = ComponentRegistry.list_all() +for category, names in all_components.items(): + print(f" {category}: {names}") + +print(f"\n总计: {ComponentRegistry.get_count()} 个组件") + +# 测试关闭verbose模式 +print("\n3. 测试关闭verbose模式:") +ComponentRegistry.set_verbose(False) + +# 尝试重新导入一个模块(应该不会打印注册信息) +print("重新导入boundary模块(应该静默):") +import importlib +import boundary +importlib.reload(boundary) + +print("\n✅ 简化导入系统测试完成!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06/time_integration.py b/example/1d-linear-convection/weno3/python/06/time_integration.py new file mode 100644 index 00000000..25ac0b4c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06/time_integration.py @@ -0,0 +1,125 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class TimeIntegratorFactory: + """时间推进器工厂""" + + @staticmethod + def create(cfd) -> 'TimeIntegrator': + """ + 创建时间推进器实例 + + Args: + cfd: CFD对象 + + Returns: + 时间推进器实例 + """ + from factories.base_factory import BaseFactory + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06a/Project.toml b/example/1d-linear-convection/weno3/python/06a/Project.toml new file mode 100644 index 00000000..48e08504 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06a/Project.toml @@ -0,0 +1,2 @@ +[deps] +PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d" diff --git a/example/1d-linear-convection/weno3/python/06a/boundary.py b/example/1d-linear-convection/weno3/python/06a/boundary.py new file mode 100644 index 00000000..6054f92d --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06a/boundary.py @@ -0,0 +1,103 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06a/cfd_registry.py b/example/1d-linear-convection/weno3/python/06a/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06a/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06a/config.py b/example/1d-linear-convection/weno3/python/06a/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06a/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06a/domain.py b/example/1d-linear-convection/weno3/python/06a/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06a/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06a/factories/base_factory.py b/example/1d-linear-convection/weno3/python/06a/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06a/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06a/flux.py b/example/1d-linear-convection/weno3/python/06a/flux.py new file mode 100644 index 00000000..beb9ed48 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06a/flux.py @@ -0,0 +1,73 @@ +""" +通量计算器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册,替代硬编码工厂 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象通量计算基类(统一接口) ---------------------- +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass + +# ---------------------- 2. 具体通量计算子类(使用装饰器注册) ---------------------- + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R + +class FluxCalculatorFactory: + """通量计算器工厂""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD对象 + + Returns: + 通量计算器实例 + """ + from factories.base_factory import BaseFactory + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06a/initial_condition.py b/example/1d-linear-convection/weno3/python/06a/initial_condition.py new file mode 100644 index 00000000..dabe7e8c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06a/initial_condition.py @@ -0,0 +1,90 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = self.config.get("pulse_center", 0.5) + width = self.config.get("pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, ic_type: str, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', ic_type, config) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06a/mesh.py b/example/1d-linear-convection/weno3/python/06a/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06a/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06a/plotter.py b/example/1d-linear-convection/weno3/python/06a/plotter.py new file mode 100644 index 00000000..9f1a414f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06a/plotter.py @@ -0,0 +1,107 @@ +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06a/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python/06a/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06a/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06a/reconstructor/base.py b/example/1d-linear-convection/weno3/python/06a/reconstructor/base.py new file mode 100644 index 00000000..bbd63850 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06a/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def reconstruct(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06a/reconstructor/eno.py b/example/1d-linear-convection/weno3/python/06a/reconstructor/eno.py new file mode 100644 index 00000000..c2fb385d --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06a/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def reconstruct(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06a/reconstructor/factory.py b/example/1d-linear-convection/weno3/python/06a/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06a/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06a/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python/06a/reconstructor/weno3.py new file mode 100644 index 00000000..6e8c3f23 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06a/reconstructor/weno3.py @@ -0,0 +1,88 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def reconstruct(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + """ + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 # smoothness indicator for stencil [v2, v3] + beta1 = (v2 - v1)**2 # smoothness indicator for stencil [v1, v2] + + d0, d1 = 2/3, 1/3 # optimal linear weights (for right value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 0.5 * v2 + 0.5 * v3 # reconstruction from [v2, v3] + q1 = -0.5 * v1 + 1.5 * v2 # reconstruction from [v1, v2] + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v3 - v2)**2 + beta1 = (v2 - v1)**2 + + d0, d1 = 1/3, 2/3 # optimal linear weights (for left value) + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + w0 = alpha0 / (alpha0 + alpha1) + w1 = alpha1 / (alpha0 + alpha1) + + q0 = 1.5 * v2 - 0.5 * v3 # from [v2, v3] + q1 = 0.5 * v1 + 0.5 * v2 # from [v1, v2] + return w0 * q0 + w1 * q1 + """ diff --git a/example/1d-linear-convection/weno3/python/06a/registry.py b/example/1d-linear-convection/weno3/python/06a/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06a/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06a/residual.py b/example/1d-linear-convection/weno3/python/06a/residual.py new file mode 100644 index 00000000..b4d4d7dc --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06a/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._reconstruct() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _reconstruct(self): + """私有方法:界面值重建""" + self.reconstructor.reconstruct(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06a/run_eno_weno.py b/example/1d-linear-convection/weno3/python/06a/run_eno_weno.py new file mode 100644 index 00000000..fd7f3874 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06a/run_eno_weno.py @@ -0,0 +1,52 @@ +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06a/solution.py b/example/1d-linear-convection/weno3/python/06a/solution.py new file mode 100644 index 00000000..92a46d3f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06a/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config.ic_type, config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06a/solver.py b/example/1d-linear-convection/weno3/python/06a/solver.py new file mode 100644 index 00000000..0d0b442d --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06a/solver.py @@ -0,0 +1,86 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + + +from flux import InviscidFluxCalculator, RusanovFluxCalculator, EngquistOsherFluxCalculator, FluxCalculatorFactory + +from boundary import BoundaryCondition, PeriodicBoundary, DirichletBoundary, NeumannBoundary, BoundaryConditionFactory + +from time_integration import TimeIntegrator, RK1Integrator, RK2Integrator, RK3Integrator, TimeIntegratorFactory + +from mesh import Mesh + +from reconstructor import ReconstructorFactory + +from initial_condition import InitialCondition, StepFunctionIC, SineWaveIC, GaussianPulseIC, InitialConditionFactory + +from domain import Domain +from solution import Solution + +from config import CfdConfig +from residual import ResidualCalculator + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config.ic_type, self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + #runge_kutta(self) + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/python/06a/time_integration.py b/example/1d-linear-convection/weno3/python/06a/time_integration.py new file mode 100644 index 00000000..25ac0b4c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06a/time_integration.py @@ -0,0 +1,125 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class TimeIntegratorFactory: + """时间推进器工厂""" + + @staticmethod + def create(cfd) -> 'TimeIntegrator': + """ + 创建时间推进器实例 + + Args: + cfd: CFD对象 + + Returns: + 时间推进器实例 + """ + from factories.base_factory import BaseFactory + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06b/boundary.py b/example/1d-linear-convection/weno3/python/06b/boundary.py new file mode 100644 index 00000000..3a271aa2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06b/boundary.py @@ -0,0 +1,104 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + diff --git a/example/1d-linear-convection/weno3/python/06b/cfd_registry.py b/example/1d-linear-convection/weno3/python/06b/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06b/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06b/config.py b/example/1d-linear-convection/weno3/python/06b/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06b/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06b/domain.py b/example/1d-linear-convection/weno3/python/06b/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06b/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06b/factories/base_factory.py b/example/1d-linear-convection/weno3/python/06b/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06b/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06b/flux/__init__.py b/example/1d-linear-convection/weno3/python/06b/flux/__init__.py new file mode 100644 index 00000000..432a36cb --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06b/flux/__init__.py @@ -0,0 +1,7 @@ +# flux/__init__.py +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 确保子模块被导入以触发注册 +from . import rusanov +from . import engquist_osher \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06b/flux/base.py b/example/1d-linear-convection/weno3/python/06b/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06b/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06b/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python/06b/flux/engquist_osher.py new file mode 100644 index 00000000..34ad5f9c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06b/flux/engquist_osher.py @@ -0,0 +1,19 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from cfd_registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06b/flux/factory.py b/example/1d-linear-convection/weno3/python/06b/flux/factory.py new file mode 100644 index 00000000..c2f6f075 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06b/flux/factory.py @@ -0,0 +1,25 @@ +# flux/factory.py +""" +通量计算器专用工厂(封装注册细节,提供清晰接口) +符合你希望“将创建逻辑封装在工厂中”的设计原则 +""" + +from factories.base_factory import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06b/flux/rusanov.py b/example/1d-linear-convection/weno3/python/06b/flux/rusanov.py new file mode 100644 index 00000000..62dd207c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06b/flux/rusanov.py @@ -0,0 +1,21 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from cfd_registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06b/initial_condition.py b/example/1d-linear-convection/weno3/python/06b/initial_condition.py new file mode 100644 index 00000000..03b803b0 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06b/initial_condition.py @@ -0,0 +1,91 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, ic_type: str, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', ic_type, config) + diff --git a/example/1d-linear-convection/weno3/python/06b/mesh.py b/example/1d-linear-convection/weno3/python/06b/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06b/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06b/plotter.py b/example/1d-linear-convection/weno3/python/06b/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06b/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06b/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python/06b/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06b/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06b/reconstructor/base.py b/example/1d-linear-convection/weno3/python/06b/reconstructor/base.py new file mode 100644 index 00000000..bbd63850 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06b/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def reconstruct(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06b/reconstructor/eno.py b/example/1d-linear-convection/weno3/python/06b/reconstructor/eno.py new file mode 100644 index 00000000..c2fb385d --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06b/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def reconstruct(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06b/reconstructor/factory.py b/example/1d-linear-convection/weno3/python/06b/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06b/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06b/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python/06b/reconstructor/weno3.py new file mode 100644 index 00000000..bf68be50 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06b/reconstructor/weno3.py @@ -0,0 +1,59 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def reconstruct(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python/06b/registry.py b/example/1d-linear-convection/weno3/python/06b/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06b/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06b/residual.py b/example/1d-linear-convection/weno3/python/06b/residual.py new file mode 100644 index 00000000..afd75222 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06b/residual.py @@ -0,0 +1,39 @@ +# residual.py + +from flux.factory import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + def compute(self): + """计算完整残差(对外唯一接口)""" + self._reconstruct() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _reconstruct(self): + """私有方法:界面值重建""" + self.reconstructor.reconstruct(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06b/run_eno_weno.py b/example/1d-linear-convection/weno3/python/06b/run_eno_weno.py new file mode 100644 index 00000000..ff46f226 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06b/run_eno_weno.py @@ -0,0 +1,54 @@ +# run_eno_weno.py + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06b/solution.py b/example/1d-linear-convection/weno3/python/06b/solution.py new file mode 100644 index 00000000..92a46d3f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06b/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config.ic_type, config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06b/solver.py b/example/1d-linear-convection/weno3/python/06b/solver.py new file mode 100644 index 00000000..d15bab2a --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06b/solver.py @@ -0,0 +1,79 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + +from boundary import BoundaryConditionFactory +from initial_condition import InitialConditionFactory +from time_integration import TimeIntegrator,TimeIntegratorFactory +from flux import InviscidFluxCalculator # 仅用于类型提示(可选) +from mesh import Mesh +from domain import Domain +from solution import Solution +from config import CfdConfig +from residual import ResidualCalculator +from reconstructor import ReconstructorFactory +from factories.base_factory import BaseFactory + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config.ic_type, self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/python/06b/time_integration.py b/example/1d-linear-convection/weno3/python/06b/time_integration.py new file mode 100644 index 00000000..25ac0b4c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06b/time_integration.py @@ -0,0 +1,125 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class TimeIntegratorFactory: + """时间推进器工厂""" + + @staticmethod + def create(cfd) -> 'TimeIntegrator': + """ + 创建时间推进器实例 + + Args: + cfd: CFD对象 + + Returns: + 时间推进器实例 + """ + from factories.base_factory import BaseFactory + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06c/boundary.py b/example/1d-linear-convection/weno3/python/06c/boundary.py new file mode 100644 index 00000000..3a271aa2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06c/boundary.py @@ -0,0 +1,104 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + diff --git a/example/1d-linear-convection/weno3/python/06c/cfd_registry.py b/example/1d-linear-convection/weno3/python/06c/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06c/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06c/config.py b/example/1d-linear-convection/weno3/python/06c/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06c/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06c/domain.py b/example/1d-linear-convection/weno3/python/06c/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06c/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06c/factories/base_factory.py b/example/1d-linear-convection/weno3/python/06c/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06c/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06c/flux/__init__.py b/example/1d-linear-convection/weno3/python/06c/flux/__init__.py new file mode 100644 index 00000000..432a36cb --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06c/flux/__init__.py @@ -0,0 +1,7 @@ +# flux/__init__.py +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 确保子模块被导入以触发注册 +from . import rusanov +from . import engquist_osher \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06c/flux/base.py b/example/1d-linear-convection/weno3/python/06c/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06c/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06c/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python/06c/flux/engquist_osher.py new file mode 100644 index 00000000..34ad5f9c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06c/flux/engquist_osher.py @@ -0,0 +1,19 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from cfd_registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06c/flux/factory.py b/example/1d-linear-convection/weno3/python/06c/flux/factory.py new file mode 100644 index 00000000..c2f6f075 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06c/flux/factory.py @@ -0,0 +1,25 @@ +# flux/factory.py +""" +通量计算器专用工厂(封装注册细节,提供清晰接口) +符合你希望“将创建逻辑封装在工厂中”的设计原则 +""" + +from factories.base_factory import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06c/flux/rusanov.py b/example/1d-linear-convection/weno3/python/06c/flux/rusanov.py new file mode 100644 index 00000000..62dd207c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06c/flux/rusanov.py @@ -0,0 +1,21 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from cfd_registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06c/initial_condition.py b/example/1d-linear-convection/weno3/python/06c/initial_condition.py new file mode 100644 index 00000000..03b803b0 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06c/initial_condition.py @@ -0,0 +1,91 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, ic_type: str, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', ic_type, config) + diff --git a/example/1d-linear-convection/weno3/python/06c/mesh.py b/example/1d-linear-convection/weno3/python/06c/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06c/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06c/plotter.py b/example/1d-linear-convection/weno3/python/06c/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06c/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06c/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python/06c/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06c/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06c/reconstructor/base.py b/example/1d-linear-convection/weno3/python/06c/reconstructor/base.py new file mode 100644 index 00000000..094b4712 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06c/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def compute_face_values(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06c/reconstructor/eno.py b/example/1d-linear-convection/weno3/python/06c/reconstructor/eno.py new file mode 100644 index 00000000..8fde4333 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06c/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def compute_face_values(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06c/reconstructor/factory.py b/example/1d-linear-convection/weno3/python/06c/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06c/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06c/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python/06c/reconstructor/weno3.py new file mode 100644 index 00000000..929061c1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06c/reconstructor/weno3.py @@ -0,0 +1,59 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python/06c/registry.py b/example/1d-linear-convection/weno3/python/06c/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06c/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06c/residual.py b/example/1d-linear-convection/weno3/python/06c/residual.py new file mode 100644 index 00000000..b1d03592 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06c/residual.py @@ -0,0 +1,39 @@ +# residual.py + +from flux.factory import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + def compute(self): + """计算完整残差(对外唯一接口)""" + self._compute_face_values() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _compute_face_values(self): + """私有方法:界面值重建""" + self.reconstructor.compute_face_values(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06c/run_eno_weno.py b/example/1d-linear-convection/weno3/python/06c/run_eno_weno.py new file mode 100644 index 00000000..ff46f226 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06c/run_eno_weno.py @@ -0,0 +1,54 @@ +# run_eno_weno.py + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06c/solution.py b/example/1d-linear-convection/weno3/python/06c/solution.py new file mode 100644 index 00000000..92a46d3f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06c/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config.ic_type, config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06c/solver.py b/example/1d-linear-convection/weno3/python/06c/solver.py new file mode 100644 index 00000000..d15bab2a --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06c/solver.py @@ -0,0 +1,79 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + +from boundary import BoundaryConditionFactory +from initial_condition import InitialConditionFactory +from time_integration import TimeIntegrator,TimeIntegratorFactory +from flux import InviscidFluxCalculator # 仅用于类型提示(可选) +from mesh import Mesh +from domain import Domain +from solution import Solution +from config import CfdConfig +from residual import ResidualCalculator +from reconstructor import ReconstructorFactory +from factories.base_factory import BaseFactory + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config.ic_type, self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/python/06c/time_integration.py b/example/1d-linear-convection/weno3/python/06c/time_integration.py new file mode 100644 index 00000000..25ac0b4c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06c/time_integration.py @@ -0,0 +1,125 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class TimeIntegratorFactory: + """时间推进器工厂""" + + @staticmethod + def create(cfd) -> 'TimeIntegrator': + """ + 创建时间推进器实例 + + Args: + cfd: CFD对象 + + Returns: + 时间推进器实例 + """ + from factories.base_factory import BaseFactory + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06d/boundary.py b/example/1d-linear-convection/weno3/python/06d/boundary.py new file mode 100644 index 00000000..3a271aa2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06d/boundary.py @@ -0,0 +1,104 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + diff --git a/example/1d-linear-convection/weno3/python/06d/cfd_registry.py b/example/1d-linear-convection/weno3/python/06d/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06d/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06d/config.py b/example/1d-linear-convection/weno3/python/06d/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06d/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06d/domain.py b/example/1d-linear-convection/weno3/python/06d/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06d/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06d/factories/base_factory.py b/example/1d-linear-convection/weno3/python/06d/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06d/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06d/flux/__init__.py b/example/1d-linear-convection/weno3/python/06d/flux/__init__.py new file mode 100644 index 00000000..432a36cb --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06d/flux/__init__.py @@ -0,0 +1,7 @@ +# flux/__init__.py +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 确保子模块被导入以触发注册 +from . import rusanov +from . import engquist_osher \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06d/flux/base.py b/example/1d-linear-convection/weno3/python/06d/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06d/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06d/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python/06d/flux/engquist_osher.py new file mode 100644 index 00000000..34ad5f9c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06d/flux/engquist_osher.py @@ -0,0 +1,19 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from cfd_registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06d/flux/factory.py b/example/1d-linear-convection/weno3/python/06d/flux/factory.py new file mode 100644 index 00000000..c2f6f075 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06d/flux/factory.py @@ -0,0 +1,25 @@ +# flux/factory.py +""" +通量计算器专用工厂(封装注册细节,提供清晰接口) +符合你希望“将创建逻辑封装在工厂中”的设计原则 +""" + +from factories.base_factory import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06d/flux/rusanov.py b/example/1d-linear-convection/weno3/python/06d/flux/rusanov.py new file mode 100644 index 00000000..62dd207c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06d/flux/rusanov.py @@ -0,0 +1,21 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from cfd_registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06d/initial_condition.py b/example/1d-linear-convection/weno3/python/06d/initial_condition.py new file mode 100644 index 00000000..500cdfb8 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06d/initial_condition.py @@ -0,0 +1,91 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', config.ic_type, config) + diff --git a/example/1d-linear-convection/weno3/python/06d/mesh.py b/example/1d-linear-convection/weno3/python/06d/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06d/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06d/plotter.py b/example/1d-linear-convection/weno3/python/06d/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06d/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06d/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python/06d/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06d/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06d/reconstructor/base.py b/example/1d-linear-convection/weno3/python/06d/reconstructor/base.py new file mode 100644 index 00000000..094b4712 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06d/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def compute_face_values(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06d/reconstructor/eno.py b/example/1d-linear-convection/weno3/python/06d/reconstructor/eno.py new file mode 100644 index 00000000..8fde4333 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06d/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def compute_face_values(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06d/reconstructor/factory.py b/example/1d-linear-convection/weno3/python/06d/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06d/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06d/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python/06d/reconstructor/weno3.py new file mode 100644 index 00000000..929061c1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06d/reconstructor/weno3.py @@ -0,0 +1,59 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python/06d/registry.py b/example/1d-linear-convection/weno3/python/06d/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06d/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06d/residual.py b/example/1d-linear-convection/weno3/python/06d/residual.py new file mode 100644 index 00000000..b1d03592 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06d/residual.py @@ -0,0 +1,39 @@ +# residual.py + +from flux.factory import FluxCalculatorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + self.reconstructor = self.cfd.reconstructor + + # 初始化通量计算器(工厂创建) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + def compute(self): + """计算完整残差(对外唯一接口)""" + self._compute_face_values() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _compute_face_values(self): + """私有方法:界面值重建""" + self.reconstructor.compute_face_values(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06d/run_eno_weno.py b/example/1d-linear-convection/weno3/python/06d/run_eno_weno.py new file mode 100644 index 00000000..ff46f226 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06d/run_eno_weno.py @@ -0,0 +1,54 @@ +# run_eno_weno.py + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06d/solution.py b/example/1d-linear-convection/weno3/python/06d/solution.py new file mode 100644 index 00000000..3d4cba56 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06d/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06d/solver.py b/example/1d-linear-convection/weno3/python/06d/solver.py new file mode 100644 index 00000000..12f0ce1e --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06d/solver.py @@ -0,0 +1,79 @@ +import numpy as np +import matplotlib.pyplot as plt +import inflect +from abc import ABC, abstractmethod + +from boundary import BoundaryConditionFactory +from initial_condition import InitialConditionFactory +from time_integration import TimeIntegrator,TimeIntegratorFactory +from flux import InviscidFluxCalculator # 仅用于类型提示(可选) +from mesh import Mesh +from domain import Domain +from solution import Solution +from config import CfdConfig +from residual import ResidualCalculator +from reconstructor import ReconstructorFactory +from factories.base_factory import BaseFactory + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.reconstructor = ReconstructorFactory.create(config, self.domain) + self.residual_calculator = ResidualCalculator(self) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/python/06d/time_integration.py b/example/1d-linear-convection/weno3/python/06d/time_integration.py new file mode 100644 index 00000000..25ac0b4c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06d/time_integration.py @@ -0,0 +1,125 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = cfd.residual_calculator + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class TimeIntegratorFactory: + """时间推进器工厂""" + + @staticmethod + def create(cfd) -> 'TimeIntegrator': + """ + 创建时间推进器实例 + + Args: + cfd: CFD对象 + + Returns: + 时间推进器实例 + """ + from factories.base_factory import BaseFactory + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06e/boundary.py b/example/1d-linear-convection/weno3/python/06e/boundary.py new file mode 100644 index 00000000..3a271aa2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06e/boundary.py @@ -0,0 +1,104 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + diff --git a/example/1d-linear-convection/weno3/python/06e/cfd_registry.py b/example/1d-linear-convection/weno3/python/06e/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06e/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06e/config.py b/example/1d-linear-convection/weno3/python/06e/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06e/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06e/domain.py b/example/1d-linear-convection/weno3/python/06e/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06e/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06e/factories/base_factory.py b/example/1d-linear-convection/weno3/python/06e/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06e/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06e/flux/__init__.py b/example/1d-linear-convection/weno3/python/06e/flux/__init__.py new file mode 100644 index 00000000..432a36cb --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06e/flux/__init__.py @@ -0,0 +1,7 @@ +# flux/__init__.py +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 确保子模块被导入以触发注册 +from . import rusanov +from . import engquist_osher \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06e/flux/base.py b/example/1d-linear-convection/weno3/python/06e/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06e/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06e/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python/06e/flux/engquist_osher.py new file mode 100644 index 00000000..34ad5f9c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06e/flux/engquist_osher.py @@ -0,0 +1,19 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from cfd_registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06e/flux/factory.py b/example/1d-linear-convection/weno3/python/06e/flux/factory.py new file mode 100644 index 00000000..c2f6f075 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06e/flux/factory.py @@ -0,0 +1,25 @@ +# flux/factory.py +""" +通量计算器专用工厂(封装注册细节,提供清晰接口) +符合你希望“将创建逻辑封装在工厂中”的设计原则 +""" + +from factories.base_factory import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06e/flux/rusanov.py b/example/1d-linear-convection/weno3/python/06e/flux/rusanov.py new file mode 100644 index 00000000..62dd207c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06e/flux/rusanov.py @@ -0,0 +1,21 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from cfd_registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06e/initial_condition.py b/example/1d-linear-convection/weno3/python/06e/initial_condition.py new file mode 100644 index 00000000..500cdfb8 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06e/initial_condition.py @@ -0,0 +1,91 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', config.ic_type, config) + diff --git a/example/1d-linear-convection/weno3/python/06e/mesh.py b/example/1d-linear-convection/weno3/python/06e/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06e/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06e/plotter.py b/example/1d-linear-convection/weno3/python/06e/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06e/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06e/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python/06e/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06e/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06e/reconstructor/base.py b/example/1d-linear-convection/weno3/python/06e/reconstructor/base.py new file mode 100644 index 00000000..094b4712 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06e/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def compute_face_values(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06e/reconstructor/eno.py b/example/1d-linear-convection/weno3/python/06e/reconstructor/eno.py new file mode 100644 index 00000000..8fde4333 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06e/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def compute_face_values(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06e/reconstructor/factory.py b/example/1d-linear-convection/weno3/python/06e/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06e/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06e/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python/06e/reconstructor/weno3.py new file mode 100644 index 00000000..929061c1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06e/reconstructor/weno3.py @@ -0,0 +1,59 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python/06e/registry.py b/example/1d-linear-convection/weno3/python/06e/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06e/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06e/residual.py b/example/1d-linear-convection/weno3/python/06e/residual.py new file mode 100644 index 00000000..e0f96cd3 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06e/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux.factory import FluxCalculatorFactory +from reconstructor import ReconstructorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + + self.reconstructor = ReconstructorFactory.create(self.config, self.domain) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._compute_face_values() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _compute_face_values(self): + """私有方法:界面值重建""" + self.reconstructor.compute_face_values(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06e/run_eno_weno.py b/example/1d-linear-convection/weno3/python/06e/run_eno_weno.py new file mode 100644 index 00000000..ff46f226 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06e/run_eno_weno.py @@ -0,0 +1,54 @@ +# run_eno_weno.py + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06e/solution.py b/example/1d-linear-convection/weno3/python/06e/solution.py new file mode 100644 index 00000000..3d4cba56 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06e/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06e/solver.py b/example/1d-linear-convection/weno3/python/06e/solver.py new file mode 100644 index 00000000..3de95b4b --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06e/solver.py @@ -0,0 +1,67 @@ +from boundary import BoundaryConditionFactory +from initial_condition import InitialConditionFactory +from time_integration import TimeIntegrator,TimeIntegratorFactory +from mesh import Mesh +from domain import Domain +from solution import Solution +from config import CfdConfig + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def exact_solution(self): + """通用对流问题的解析解:u(x, T) = u0(x - c*T),周期边界""" + x = self.domain.mesh.xcc + T = self.config.final_time + c = self.config.wave_speed + L = self.domain.mesh.L + + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * T + L) % L + + # 获取 IC 实例并评估 + ic = InitialConditionFactory.create(self.config) + return ic.evaluate_at(x_shifted) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + # 整理标准化结果 + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + self.result = { + "x": domain.mesh.xcc, + "numerical": u_numerical, + "analytical": self.exact_solution(), + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } + + return u_numerical diff --git a/example/1d-linear-convection/weno3/python/06e/time_integration.py b/example/1d-linear-convection/weno3/python/06e/time_integration.py new file mode 100644 index 00000000..51b2b20c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06e/time_integration.py @@ -0,0 +1,126 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component +from residual import ResidualCalculator + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = ResidualCalculator(cfd) + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class TimeIntegratorFactory: + """时间推进器工厂""" + + @staticmethod + def create(cfd) -> 'TimeIntegrator': + """ + 创建时间推进器实例 + + Args: + cfd: CFD对象 + + Returns: + 时间推进器实例 + """ + from factories.base_factory import BaseFactory + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06f/boundary.py b/example/1d-linear-convection/weno3/python/06f/boundary.py new file mode 100644 index 00000000..3a271aa2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06f/boundary.py @@ -0,0 +1,104 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + diff --git a/example/1d-linear-convection/weno3/python/06f/cfd_registry.py b/example/1d-linear-convection/weno3/python/06f/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06f/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06f/config.py b/example/1d-linear-convection/weno3/python/06f/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06f/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06f/domain.py b/example/1d-linear-convection/weno3/python/06f/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06f/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06f/factories/base_factory.py b/example/1d-linear-convection/weno3/python/06f/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06f/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06f/flux/__init__.py b/example/1d-linear-convection/weno3/python/06f/flux/__init__.py new file mode 100644 index 00000000..432a36cb --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06f/flux/__init__.py @@ -0,0 +1,7 @@ +# flux/__init__.py +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 确保子模块被导入以触发注册 +from . import rusanov +from . import engquist_osher \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06f/flux/base.py b/example/1d-linear-convection/weno3/python/06f/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06f/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06f/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python/06f/flux/engquist_osher.py new file mode 100644 index 00000000..34ad5f9c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06f/flux/engquist_osher.py @@ -0,0 +1,19 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from cfd_registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06f/flux/factory.py b/example/1d-linear-convection/weno3/python/06f/flux/factory.py new file mode 100644 index 00000000..c2f6f075 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06f/flux/factory.py @@ -0,0 +1,25 @@ +# flux/factory.py +""" +通量计算器专用工厂(封装注册细节,提供清晰接口) +符合你希望“将创建逻辑封装在工厂中”的设计原则 +""" + +from factories.base_factory import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06f/flux/rusanov.py b/example/1d-linear-convection/weno3/python/06f/flux/rusanov.py new file mode 100644 index 00000000..62dd207c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06f/flux/rusanov.py @@ -0,0 +1,21 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from cfd_registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06f/initial_condition.py b/example/1d-linear-convection/weno3/python/06f/initial_condition.py new file mode 100644 index 00000000..893034c7 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06f/initial_condition.py @@ -0,0 +1,103 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def exact_solution(self, cfd): + """ + 默认解析解:线性对流 u(x, t) = u0(x - c * t) + 子类可重写以支持更复杂物理 + """ + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * t + L) % L + return self.evaluate_at(x_shifted) + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', config.ic_type, config) + diff --git a/example/1d-linear-convection/weno3/python/06f/mesh.py b/example/1d-linear-convection/weno3/python/06f/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06f/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06f/plotter.py b/example/1d-linear-convection/weno3/python/06f/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06f/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06f/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python/06f/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06f/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06f/reconstructor/base.py b/example/1d-linear-convection/weno3/python/06f/reconstructor/base.py new file mode 100644 index 00000000..094b4712 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06f/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def compute_face_values(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06f/reconstructor/eno.py b/example/1d-linear-convection/weno3/python/06f/reconstructor/eno.py new file mode 100644 index 00000000..8fde4333 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06f/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def compute_face_values(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06f/reconstructor/factory.py b/example/1d-linear-convection/weno3/python/06f/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06f/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06f/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python/06f/reconstructor/weno3.py new file mode 100644 index 00000000..929061c1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06f/reconstructor/weno3.py @@ -0,0 +1,59 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python/06f/registry.py b/example/1d-linear-convection/weno3/python/06f/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06f/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06f/residual.py b/example/1d-linear-convection/weno3/python/06f/residual.py new file mode 100644 index 00000000..e0f96cd3 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06f/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux.factory import FluxCalculatorFactory +from reconstructor import ReconstructorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + + self.reconstructor = ReconstructorFactory.create(self.config, self.domain) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._compute_face_values() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _compute_face_values(self): + """私有方法:界面值重建""" + self.reconstructor.compute_face_values(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06f/run_eno_weno.py b/example/1d-linear-convection/weno3/python/06f/run_eno_weno.py new file mode 100644 index 00000000..ff46f226 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06f/run_eno_weno.py @@ -0,0 +1,54 @@ +# run_eno_weno.py + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06f/solution.py b/example/1d-linear-convection/weno3/python/06f/solution.py new file mode 100644 index 00000000..3d4cba56 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06f/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06f/solver.py b/example/1d-linear-convection/weno3/python/06f/solver.py new file mode 100644 index 00000000..8b369fee --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06f/solver.py @@ -0,0 +1,57 @@ +from boundary import BoundaryConditionFactory +from initial_condition import InitialConditionFactory +from time_integration import TimeIntegrator,TimeIntegratorFactory +from mesh import Mesh +from domain import Domain +from solution import Solution +from config import CfdConfig + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + self.result = self._assemble_result() + return self.result["numerical"] + + def _assemble_result(self): + """组装标准化求解结果字典""" + u_numerical = self.solution.u[self.domain.ist:self.domain.ied].copy() + ic = InitialConditionFactory.create(self.config) + analytical = ic.exact_solution(self) + return { + "x": self.domain.mesh.xcc, + "numerical": u_numerical, + "analytical": analytical, + "config": { + "scheme": self.config.recon_scheme, + "order": self.config.spatial_order, + "rk_order": self.config.rk_order, + "final_time": self.config.final_time + } + } \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06f/time_integration.py b/example/1d-linear-convection/weno3/python/06f/time_integration.py new file mode 100644 index 00000000..51b2b20c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06f/time_integration.py @@ -0,0 +1,126 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component +from residual import ResidualCalculator + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = ResidualCalculator(cfd) + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class TimeIntegratorFactory: + """时间推进器工厂""" + + @staticmethod + def create(cfd) -> 'TimeIntegrator': + """ + 创建时间推进器实例 + + Args: + cfd: CFD对象 + + Returns: + 时间推进器实例 + """ + from factories.base_factory import BaseFactory + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06g/boundary.py b/example/1d-linear-convection/weno3/python/06g/boundary.py new file mode 100644 index 00000000..3a271aa2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/boundary.py @@ -0,0 +1,104 @@ +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 边界条件抽象基类(统一接口) ---------------------- +class BoundaryCondition(ABC): + """边界条件抽象基类:定义所有边界条件必须实现的接口""" + def __init__(self, cfd): + self.cfd = cfd + self.domain = cfd.domain + self.config = cfd.config # 可从配置读取边界参数(如进口速度、固壁温度等) + + @abstractmethod + def apply(self, u): + """ + 应用边界条件到解数组 + :param u: 包含ghost层的解数组(会直接修改该数组) + :return: None + """ + pass + +# ---------------------- 具体边界条件实现(使用装饰器注册)-------------------- + +@register_component('boundary', 'periodic') +class PeriodicBoundary(BoundaryCondition): + """周期边界条件(1D专用)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左ghost层 = 右物理层 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ied - 1 - ig] + + # 右ghost层 = 左物理层 + for ig in range(nghosts): + u[ied + ig] = u[ist + ig] + +@register_component('boundary', 'dirichlet') +class DirichletBoundary(BoundaryCondition): + """Dirichlet(固定值)边界条件(如进口固定速度、固壁零速度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # ✅ 修复:使用 getattr 而不是 .get() 方法 + # 旧代码: left_value = self.config.get("left_boundary_value", 1.0) + # 新代码: 使用 getattr,它对于类和实例都适用 + left_value = getattr(self.config, "left_boundary_value", 1.0) + + # 左边界(进口)固定值 + for ig in range(nghosts): + u[ist - 1 - ig] = left_value + + # ✅ 修复:同样使用 getattr + right_value = getattr(self.config, "right_boundary_value", 2.0) + + # 右边界(出口)固定值 + for ig in range(nghosts): + u[ied + ig] = right_value + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Dirichlet边界: 左值={left_value}, 右值={right_value}") + +@register_component('boundary', 'neumann') +class NeumannBoundary(BoundaryCondition): + """Neumann(零梯度)边界条件(如出口无梯度)""" + def apply(self, u): + nghosts = self.domain.nghosts + ist = self.domain.ist + ied = self.domain.ied + + # 左边界零梯度 + for ig in range(nghosts): + u[ist - 1 - ig] = u[ist + ig] + + # 右边界零梯度 + for ig in range(nghosts): + u[ied + ig] = u[ied - 1 - ig] + + # 调试信息 + if hasattr(self.config, 'debug') and self.config.debug: + print(f" 应用Neumann边界: 零梯度") + +class BoundaryConditionFactory: + """边界条件工厂""" + + @staticmethod + def create(cfd) -> 'BoundaryCondition': + """ + 创建边界条件实例 + + Args: + cfd: CFD对象 + + Returns: + 边界条件实例 + """ + from factories.base_factory import BaseFactory + bc_type = cfd.config.boundary_type + return BaseFactory.create_component('boundary', bc_type, cfd) + diff --git a/example/1d-linear-convection/weno3/python/06g/cfd_registry.py b/example/1d-linear-convection/weno3/python/06g/cfd_registry.py new file mode 100644 index 00000000..c12eb106 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/cfd_registry.py @@ -0,0 +1,62 @@ +# cfd_registry.py +""" +CFD注册系统统一导入模块 +所有其他模块从这里导入注册系统 +""" + +import sys +import os + +# 添加当前目录到路径,确保能找到registry.py +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +try: + # 尝试导入注册系统 + from registry import ComponentRegistry, register_component + REGISTRY_AVAILABLE = True +except ImportError: + # 如果找不到,提供简单的空实现 + print("⚠️ 警告: 未找到 registry.py,使用最小化回退实现") + + class ComponentRegistry: + """最小化的注册系统回退实现""" + _registries = {} + + @classmethod + def register(cls, category, name, component_class): + if category not in cls._registries: + cls._registries[category] = {} + cls._registries[category][name] = component_class + + @classmethod + def get(cls, category, name): + if category not in cls._registries: + raise ValueError(f"未知类别: {category}") + if name not in cls._registries[category]: + raise ValueError(f"未找到: {category}.{name}") + return cls._registries[category][name] + + @classmethod + def create(cls, category, name, *args, **kwargs): + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls): + return {cat: list(comps.keys()) + for cat, comps in cls._registries.items()} + + def register_component(category, name=None): + """简化的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator + + REGISTRY_AVAILABLE = False + +# 导出统一的接口 +__all__ = ['ComponentRegistry', 'register_component', 'REGISTRY_AVAILABLE'] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06g/config.py b/example/1d-linear-convection/weno3/python/06g/config.py new file mode 100644 index 00000000..b3ad4749 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/config.py @@ -0,0 +1,41 @@ +# config.py +class CfdConfig: + def __init__(self): + self.ic_type = "step" + self.recon_scheme = "eno" # 0=ENO, 1=WENO + self.flux_type = "rusanov" # 0=Rusanov, 1=Engquist-Osher + self.rk_order = 1 + self.wave_speed = 1.0 + self.final_time = 0.625 + self.dt = 0.025 + + self.boundary_type = "periodic" + self.left_boundary_value = 1.0 # Dirichlet左边界值 + self.right_boundary_value = 2.0 # Dirichlet右边界值 + + self.spatial_order = 2 + + def with_reconstruction(self, scheme, order=None): + """专用配置:重建方案(链式调用)""" + self.recon_scheme = scheme.lower() # 统一小写,避免大小写问题 + + # 智能默认阶数 + if order is not None: + self.spatial_order = order + else: + if self.recon_scheme.startswith("weno"): + self.spatial_order = 5 + elif self.recon_scheme == "eno": + self.spatial_order = 3 # ENO默认3阶 + else: + raise ValueError(f"不支持的重建格式:{scheme}(仅支持 eno/weno)") + + return self + + def with_boundary(self, bc_type, left_value=None, right_value=None): + self.boundary_type = bc_type + if left_value is not None: + self.left_boundary_value = left_value + if right_value is not None: + self.right_boundary_value = right_value + return self \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06g/domain.py b/example/1d-linear-convection/weno3/python/06g/domain.py new file mode 100644 index 00000000..ae1cca4e --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/domain.py @@ -0,0 +1,56 @@ +# domain.py +from mesh import Mesh + +class Domain: + """计算域:管理物理区域、ghost层、索引映射等逻辑,依赖 Mesh 提供几何信息""" + def __init__(self, config, mesh): + """ + 初始化计算域 + :param mesh: Mesh实例(静态网格属性) + :param config: CfdConfig实例(包含recon_scheme/spatial_order) + """ + self.config = config + self.mesh = mesh + + # 核心:根据重建格式动态计算nghosts + self.nghosts = self._calc_nghosts() + + # 基于nghosts推导索引 + self.ist = self.nghosts # 物理网格起始索引 + self.ied = self.ist + mesh.ncells # 物理网格结束索引 + self.ntcells = mesh.ncells + 2 * self.nghosts # 总网格数(含ghost) + + # 可选:调试信息(可后续移除) + # print(f"mesh.ncells={mesh.ncells}") + # print(f"self.config.spatial_order={self.config.spatial_order}") + # print(f"self.nghosts={self.nghosts}") + # print(f"self.ist={self.ist}") + # print(f"self.ied={self.ied}") + + def _calc_nghosts(self): + """内部方法:根据重建格式和阶数计算ghost层数量""" + scheme = self.config.recon_scheme.lower() + order = self.config.spatial_order + + if scheme is None: + raise ValueError("必须先通过 with_reconstruction 设置重建格式!") + + # ✅ 统一处理:所有以 "weno" 开头的都按 WENO 规则计算 ghost 层数 + if scheme == "eno": + nghosts = order + elif scheme.startswith("weno"): # ← 关键修改:支持 "weno", "weno3", "weno5", "weno-z" 等 + nghosts = order // 2 + 1 + else: + raise ValueError(f"未知重建格式 {scheme},无法计算ghost层!") + + if nghosts <= 0: + raise ValueError(f"计算得到的ghost层数量无效:{nghosts}(阶数{order},格式{scheme})") + return nghosts + + def is_physical_cell(self, idx): + """判断索引是否在物理网格范围内""" + return self.ist <= idx < self.ied + + def get_physical_indices(self): + """返回物理网格的索引范围(可直接用于循环)""" + return range(self.ist, self.ied) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06g/factories/base_factory.py b/example/1d-linear-convection/weno3/python/06g/factories/base_factory.py new file mode 100644 index 00000000..7b8b6105 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/factories/base_factory.py @@ -0,0 +1,49 @@ +# factories/base_factory.py +""" +工厂基类 - 提供统一的组件创建接口 +""" + +from cfd_registry import ComponentRegistry +from typing import Any, Optional + +class BaseFactory: + """工厂基类,提供统一的组件创建方法""" + + @classmethod + def create_component(cls, category: str, name: str, *args, **kwargs) -> Any: + """ + 统一创建组件的方法 + + Args: + category: 组件类别(如'boundary', 'flux'等) + name: 组件名称(如'periodic', 'rusanov'等) + *args, **kwargs: 传递给构造函数的参数 + + Returns: + 创建的组件实例 + + Raises: + ValueError: 如果组件不存在 + """ + name_lower = name.lower() + + try: + return ComponentRegistry.create(category, name_lower, *args, **kwargs) + except ValueError as e: + # 获取可用组件列表 + available = ComponentRegistry.list_all().get(category, []) + + # 构建友好的错误信息 + if available: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"可用类型:{available}") + else: + error_msg = (f"不支持的{category}类型:'{name}'\n" + f"({category}类别下没有注册任何组件)") + + raise ValueError(error_msg) from e + + @classmethod + def get_available_components(cls, category: str) -> list: + """获取指定类别的可用组件列表""" + return ComponentRegistry.list_all().get(category, []) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06g/flux/__init__.py b/example/1d-linear-convection/weno3/python/06g/flux/__init__.py new file mode 100644 index 00000000..432a36cb --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/flux/__init__.py @@ -0,0 +1,7 @@ +# flux/__init__.py +from .base import InviscidFluxCalculator +from .factory import FluxCalculatorFactory + +# 确保子模块被导入以触发注册 +from . import rusanov +from . import engquist_osher \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06g/flux/base.py b/example/1d-linear-convection/weno3/python/06g/flux/base.py new file mode 100644 index 00000000..7a5a134f --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/flux/base.py @@ -0,0 +1,25 @@ +# flux/base.py +""" +抽象通量计算基类 +""" + +from abc import ABC, abstractmethod + +class InviscidFluxCalculator(ABC): + """无粘通量计算抽象基类:定义一维CFD通量计算接口""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.mesh = cfd.domain.mesh + self.wave_speed = self.config.wave_speed + + @abstractmethod + def compute(self, q_face_left, q_face_right, flux): + """ + 计算无粘通量(核心接口) + :param q_face_left: 左界面值数组 + :param q_face_right: 右界面值数组 + :param flux: 输出通量数组 + :return: None + """ + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06g/flux/engquist_osher.py b/example/1d-linear-convection/weno3/python/06g/flux/engquist_osher.py new file mode 100644 index 00000000..34ad5f9c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/flux/engquist_osher.py @@ -0,0 +1,19 @@ +# flux/engquist_osher.py +""" +Engquist-Osher 通量计算器(线性对流专用) +""" + +from cfd_registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'engquist-osher') +class EngquistOsherFluxCalculator(InviscidFluxCalculator): + """Engquist-Osher通量(线性对流专用)""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + c = self.wave_speed + cp = 0.5 * (c + abs(c)) + cm = 0.5 * (c - abs(c)) + u_L = q_face_left[i] + u_R = q_face_right[i] + flux[i] = cp * u_L + cm * u_R \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06g/flux/factory.py b/example/1d-linear-convection/weno3/python/06g/flux/factory.py new file mode 100644 index 00000000..c2f6f075 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/flux/factory.py @@ -0,0 +1,25 @@ +# flux/factory.py +""" +通量计算器专用工厂(封装注册细节,提供清晰接口) +符合你希望“将创建逻辑封装在工厂中”的设计原则 +""" + +from factories.base_factory import BaseFactory + +class FluxCalculatorFactory: + """通量计算器工厂:隐藏注册类别和组件名称等细节""" + + @staticmethod + def create(cfd) -> 'InviscidFluxCalculator': + """ + 创建通量计算器实例 + + Args: + cfd: CFD 主对象,包含 config.flux_type 等配置 + + Returns: + 实现 InviscidFluxCalculator 接口的通量计算器实例 + """ + flux_type = cfd.config.flux_type + return BaseFactory.create_component('flux', flux_type, cfd) + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06g/flux/rusanov.py b/example/1d-linear-convection/weno3/python/06g/flux/rusanov.py new file mode 100644 index 00000000..62dd207c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/flux/rusanov.py @@ -0,0 +1,21 @@ +# flux/rusanov.py +""" +Rusanov(Lax-Friedrichs)通量计算器 +""" + +from cfd_registry import register_component +from .base import InviscidFluxCalculator + +@register_component('flux', 'rusanov') +class RusanovFluxCalculator(InviscidFluxCalculator): + """Rusanov(Lax-Friedrichs)通量""" + def compute(self, q_face_left, q_face_right, flux): + for i in range(self.mesh.nnodes): + u_L = q_face_left[i] + u_R = q_face_right[i] + c_L = self.wave_speed + c_R = self.wave_speed + F_L = c_L * u_L # Flux from left state + F_R = c_R * u_R # Flux from right state + Smax = max(abs(c_L), abs(c_R)) # Maximum wave speed + flux[i] = 0.5 * (F_L + F_R) - 0.5 * Smax * (u_R - u_L) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06g/initial_condition.py b/example/1d-linear-convection/weno3/python/06g/initial_condition.py new file mode 100644 index 00000000..963e9cdd --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/initial_condition.py @@ -0,0 +1,107 @@ +# initial_condition.py + +""" +初始条件模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +import numpy as np +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 初始条件抽象基类 ---------------------- +class InitialCondition(ABC): + """初始条件基类""" + def __init__(self, config): + self.config = config + + @abstractmethod + def apply(self, solution): + """将初始条件应用到 solution 的内部区域""" + pass + + @abstractmethod + def evaluate_at(self, x): + """纯数学函数:给定 x,返回 u(x),不涉及网格或边界""" + pass + + def exact_solution(self, cfd): + """ + 默认解析解:线性对流 u(x, t) = u0(x - c * t) + 子类可重写以支持更复杂物理 + """ + x = cfd.domain.mesh.xcc + t = cfd.config.final_time + c = cfd.config.wave_speed + L = cfd.domain.mesh.L + # 周期平移:确保在 [x0, x0 + L) 内 + x_shifted = (x - c * t + L) % L + return self.evaluate_at(x_shifted) + + def _apply_to_interior(self, solution, values): + domain = solution.domain + for i in range(domain.ist, domain.ied): + j = i - domain.ist + solution.u[i] = values[j] + + +# ---------------------- 2. 具体初始条件实现(使用装饰器注册) ---------------------- + +@register_component('initial_condition', 'step') +class StepFunctionIC(InitialCondition): + def evaluate_at(self, x): + u0 = np.ones_like(x) + mask = (x >= 0.5) & (x <= 1.0) + u0[mask] = 2.0 + return u0 + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'sin') +class SineWaveIC(InitialCondition): + def evaluate_at(self, x): + L = getattr(self.config, "domain_length", 2.0) + return np.sin(2 * np.pi * x / L) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +@register_component('initial_condition', 'gaussian') +class GaussianPulseIC(InitialCondition): + def evaluate_at(self, x): + center = getattr(self.config, "pulse_center", 0.5) + width = getattr(self.config, "pulse_width", 0.1) + return np.exp(-((x - center) / width) ** 2) + + def apply(self, solution): + x = solution.domain.mesh.xcc + u0 = self.evaluate_at(x) + self._apply_to_interior(solution, u0) + +class InitialConditionFactory: + """初始条件工厂""" + + @classmethod + def create(cls, config) -> 'InitialCondition': + """ + 创建初始条件实例 + + Args: + ic_type: 初始条件类型(如'step', 'sin'等) + config: 配置对象 + + Returns: + 初始条件实例 + """ + from factories.base_factory import BaseFactory + return BaseFactory.create_component('initial_condition', config.ic_type, config) + + @classmethod + def get_exact_solution(cls, cfd): + return cls.create(cfd.config).exact_solution(cfd) # 一行! + diff --git a/example/1d-linear-convection/weno3/python/06g/mesh.py b/example/1d-linear-convection/weno3/python/06g/mesh.py new file mode 100644 index 00000000..bb855313 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/mesh.py @@ -0,0 +1,26 @@ +# mesh.py +import numpy as np + +# Mesh class: defines computational grid +class Mesh: + def __init__(self): + self.xmin = 0.0 + self.xmax = 2.0 + self.ncells = 40 + self.nnodes = self.ncells + 1 + self.nx = self.ncells + self.x = np.zeros(self.nnodes) + self.xcc = np.zeros(self.ncells) + self.init_mesh() + + def init_mesh(self): + self.L = self.xmax - self.xmin + self.dx = self.L / self.ncells + + # Generate node coordinates + for i in range(self.nnodes): + self.x[i] = self.xmin + i * self.dx + + # Generate cell center coordinates + for i in range(self.ncells): + self.xcc[i] = 0.5 * (self.x[i] + self.x[i+1]) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06g/plotter.py b/example/1d-linear-convection/weno3/python/06g/plotter.py new file mode 100644 index 00000000..e1f99ae2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/plotter.py @@ -0,0 +1,109 @@ +# plotter.py + +import matplotlib.pyplot as plt +import numpy as np +import inflect + +class CFDPlotter: + """CFD可视化工具类:解耦绘图逻辑""" + def __init__(self): + # 预设样式(统一管理) + self.default_styles = { + "numerical": {"color": "blue", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + "analytical": {"color": "red", "linestyle": "--", "marker": "", "linewidth": 1.5}, + "comparison": [ + {"color": "black", "linestyle": "-", "marker": "o", "markerfacecolor": "none"}, + {"color": "blue", "linestyle": "--", "marker": "s", "markerfacecolor": "none"}, + {"color": "green", "linestyle": ":", "marker": "^", "markerfacecolor": "none"}, + ] + } + self.p = inflect.engine() + + def plot_quick(self, cfd_result, title=None, show=True, save_path=None): + """轻量即时绘图(快速验证结果)""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + rk_str = self.p.ordinal(cfd_result["config"]["rk_order"]) + title = (f'1D Convection (t={cfd_result["config"]["final_time"]:.3f})\n' + f'{cfd_result["config"]["order"]}th-order {cfd_result["config"]["scheme"].upper()} + {rk_str}-order RK') + + # 绘制数值解 + plt.plot( + cfd_result["x"], cfd_result["numerical"], + label=f'Numerical ({cfd_result["config"]["scheme"].upper()})', + **self.default_styles["numerical"], + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + cfd_result["x"], cfd_result["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def plot_comparison(self, result_list, title=None, show=True, save_path=None): + """多格式/多精度对比绘图""" + plt.figure("OneFLOW-CFD Solver",figsize=(10, 6)) + + # 自动生成标题 + if title is None: + schemes = [f'{r["config"]["scheme"].upper()}{r["config"]["order"]}' for r in result_list] + rk_str = self.p.ordinal(result_list[0]["config"]["rk_order"]) + title = (f'1D Convection Comparison (t={result_list[0]["config"]["final_time"]:.3f})\n' + f'{", ".join(schemes)} + {rk_str}-order RK') + + # 绘制多个数值解 + for i, res in enumerate(result_list): + style = self.default_styles["comparison"][i % len(self.default_styles["comparison"])] + label = f'Numerical ({res["config"]["scheme"].upper()}{res["config"]["order"]})' + plt.plot( + res["x"], res["numerical"], + label=label, + **style, + markersize=5, linewidth=0.5 + ) + + # 绘制解析解 + plt.plot( + result_list[0]["x"], result_list[0]["analytical"], + label='Analytical', + **self.default_styles["analytical"] + ) + + # 通用样式 + self._set_common_style(title) + + if save_path: + plt.savefig(save_path, dpi=300, bbox_inches='tight') + if show: + plt.show() + plt.close() + + def _set_common_style(self, title): + """统一设置图表样式""" + plt.title(title, fontsize=12) + plt.xlabel('x', fontsize=10) + plt.ylabel('u', fontsize=10) + plt.legend(fontsize=9) + plt.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7) + plt.tight_layout() + +# 快捷函数:ENO/WENO对比绘图 +def plot_eno_weno_comparison(eno_result, weno_result, save_path=None): + plotter = CFDPlotter() + plotter.plot_comparison( + result_list=[eno_result, weno_result], + save_path=save_path + ) \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06g/reconstructor/__init__.py b/example/1d-linear-convection/weno3/python/06g/reconstructor/__init__.py new file mode 100644 index 00000000..46a023a2 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/reconstructor/__init__.py @@ -0,0 +1,4 @@ +# reconstructor/__init__.py +from .factory import ReconstructorFactory +from .base import Reconstructor +# 注意:我们不直接导入具体的重构器类,让工厂动态加载 \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06g/reconstructor/base.py b/example/1d-linear-convection/weno3/python/06g/reconstructor/base.py new file mode 100644 index 00000000..094b4712 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/reconstructor/base.py @@ -0,0 +1,7 @@ +# reconstructor/base.py +from abc import ABC, abstractmethod + +class Reconstructor(ABC): + @abstractmethod + def compute_face_values(self, q, cfd): + pass \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06g/reconstructor/eno.py b/example/1d-linear-convection/weno3/python/06g/reconstructor/eno.py new file mode 100644 index 00000000..8fde4333 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/reconstructor/eno.py @@ -0,0 +1,90 @@ +# reconstructor/eno.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +# ---------------------- 1. 重构系数初始化函数 ---------------------- +def _init_eno_coef(spatial_order, coef): + """Initialize reconstruction coefficients for different spatial orders.""" + if spatial_order == 1: + coef[0] = [1.0] + coef[1] = [1.0] + elif spatial_order == 2: + coef[0] = [3.0/2.0, -1.0/2.0] + coef[1] = [1.0/2.0, 1.0/2.0] + coef[2] = [-1.0/2.0, 3.0/2.0] + elif spatial_order == 3: + coef[0] = [ 11.0/6.0, -7.0/6.0, 1.0/3.0 ] + coef[1] = [ 1.0/3.0, 5.0/6.0, -1.0/6.0 ] + coef[2] = [ -1.0/6.0, 5.0/6.0, 1.0/3.0 ] + coef[3] = [ 1.0/3.0, -7.0/6.0, 11.0/6.0 ] + elif spatial_order == 4: + coef[0] = [ 25.0/12.0, -23.0/12.0, 13.0/12.0, -1.0/4.0 ] + coef[1] = [ 1.0/4.0, 13.0/12.0, -5.0/12.0, 1.0/12.0 ] + coef[2] = [ -1.0/12.0, 7.0/12.0, 7.0/12.0, -1.0/12.0 ] + coef[3] = [ 1.0/12.0, -5.0/12.0, 13.0/12.0, 1.0/4.0 ] + coef[4] = [ -1.0/4.0, 13.0/12.0, -23.0/12.0, 25.0/12.0 ] + elif spatial_order == 5: + coef[0] = [ 137.0/60.0, -163.0/60.0, 137.0/60.0, -21.0/20.0, 1.0/5.0 ] + coef[1] = [ 1.0/5.0, 77.0/60.0, -43.0/60.0, 17.0/60.0, -1.0/20.0 ] + coef[2] = [ -1.0/20.0, 9.0/20.0, 47.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[3] = [ 1.0/30.0, -13.0/60.0, 47.0/60.0, 9.0/20.0, -1.0/20.0 ] + coef[4] = [ -1.0/20.0, 17.0/60.0, -43.0/60.0, 77.0/60.0, 1.0/5.0 ] + coef[5] = [ 1.0/5.0, -21.0/20.0, 137.0/60.0, -163.0/60.0, 137.0/60.0 ] + elif spatial_order == 6: + coef[0] = [ 49.0/20.0, -71.0/20.0, 79.0/20.0, -163.0/60.0, 31.0/30.0, -1.0/6.0 ] + coef[1] = [ 1.0/6.0, 29.0/20.0, -21.0/20.0, 37.0/60.0, -13.0/60.0, 1.0/30.0 ] + coef[2] = [ -1.0/30.0, 11.0/30.0, 19.0/20.0, -23.0/60.0, 7.0/60.0, -1.0/60.0 ] + coef[3] = [ 1.0/60.0, -2.0/15.0, 37.0/60.0, 37.0/60.0, -2.0/15.0, 1.0/60.0 ] + coef[4] = [ -1.0/60.0, 7.0/60.0, -23.0/60.0, 19.0/20.0, 11.0/30.0, -1.0/30.0 ] + coef[5] = [ 1.0/30.0, -13.0/60.0, 37.0/60.0, -21.0/20.0, 29.0/20.0, 1.0/6.0 ] + coef[6] = [ -1.0/6.0, 31.0/30.0, -163.0/60.0, 79.0/20.0, -71.0/20.0, 49.0/20.0 ] + elif spatial_order == 7: + coef[0] = [ 363.0/140.0, -617.0/140.0, 853.0/140.0, -2341.0/420.0, 667.0/210.0, -43.0/42.0, 1.0/7.0 ] + coef[1] = [ 1.0/7.0, 223.0/140.0, -197.0/140.0, 153.0/140.0, -241.0/420.0, 37.0/210.0, -1.0/42.0 ] + coef[2] = [ -1.0/42.0, 13.0/42.0, 153.0/140.0, -241.0/420.0, 109.0/420.0, -31.0/420.0, 1.0/105.0 ] + coef[3] = [ 1.0/105.0, -19.0/210.0, 107.0/210.0, 319.0/420.0, -101.0/420.0, 5.0/84.0, -1.0/140.0 ] + coef[4] = [ -1.0/140.0, 5.0/84.0, -101.0/420.0, 319.0/420.0, 107.0/210.0, -19.0/210.0, 1.0/105.0 ] + coef[5] = [ 1.0/105.0, -31.0/420.0, 109.0/420.0, -241.0/420.0, 153.0/140.0, 13.0/42.0, -1.0/42.0 ] + coef[6] = [ -1.0/42.0, 37.0/210.0, -241.0/420.0, 153.0/140.0, -197.0/140.0, 223.0/140.0, 1.0/7.0 ] + coef[7] = [ 1.0/7.0, -43.0/42.0, 667.0/210.0, -2341.0/420.0, 853.0/140.0, -617.0/140.0, 363.0/140.0 ] + +# ---------------------- 2. ENO 重构器 ---------------------- + +@register_component('reconstructor', 'eno') +class EnoReconstructor(Reconstructor): + def __init__(self, spatial_order, ntcells): + self.spatial_order = spatial_order + self.ntcells = ntcells + self.lmc = np.zeros(self.ntcells, dtype=int) + self.coef = np.zeros((spatial_order + 1, spatial_order)) + self.dd = np.zeros((spatial_order, self.ntcells)) + _init_eno_coef(self.spatial_order, self.coef) + + def compute_face_values(self, q, cfd): + """ENO reconstruction of interface values""" + self.dd[0, :] = q + for m in range(1, self.spatial_order): + for j in range(self.ntcells - m): + self.dd[m, j] = self.dd[m-1, j+1] - self.dd[m-1, j] + + domain = cfd.domain + solution = cfd.solution + + for i in range(domain.ist - 1, domain.ied + 1): + self.lmc[i] = i + for m in range(1, self.spatial_order): + if abs(self.dd[m, self.lmc[i] - 1]) < abs(self.dd[m, self.lmc[i]]): + self.lmc[i] -= 1 + + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + k1 = self.lmc[i - 1] + k2 = self.lmc[i] + r1 = i - 1 - k1 + r2 = i - k2 + solution.q_face_left[j] = 0.0 + solution.q_face_right[j] = 0.0 + for m in range(self.spatial_order): + solution.q_face_left[j] += q[k1 + m] * self.coef[r1 + 1, m] + solution.q_face_right[j] += q[k2 + m] * self.coef[r2, m] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06g/reconstructor/factory.py b/example/1d-linear-convection/weno3/python/06g/reconstructor/factory.py new file mode 100644 index 00000000..efa4a144 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/reconstructor/factory.py @@ -0,0 +1,42 @@ +# reconstructor/factory.py +from .eno import EnoReconstructor +from .weno3 import Weno3Reconstructor +from cfd_registry import ComponentRegistry + +class ReconstructorFactory: + @staticmethod + def create(config, domain): + scheme = config.recon_scheme.lower() + order = getattr(config, 'spatial_order', None) + + if scheme == "weno": + if order is None: + raise ValueError("使用 'weno' 时必须设置 config.spatial_order") + scheme = f"weno{order}" + + # 使用BaseFactory,但处理特殊参数 + from factories.base_factory import BaseFactory + + try: + if scheme == "eno": + if order is None: + order = 3 + # ENO需要特殊参数 + return ComponentRegistry.create('reconstructor', scheme, order, domain.ntcells) + elif scheme == "weno3": + # WENO3无参数 + return BaseFactory.create_component('reconstructor', scheme) + else: + # 其他情况 + return BaseFactory.create_component('reconstructor', scheme, config) + except ValueError as e: + # 简单的回退逻辑 + if scheme == "eno": + if order is None: + order = 3 + from .eno import EnoReconstructor + return EnoReconstructor(order, domain.ntcells) + elif scheme == "weno3": + from .weno3 import Weno3Reconstructor + return Weno3Reconstructor() + raise \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06g/reconstructor/weno3.py b/example/1d-linear-convection/weno3/python/06g/reconstructor/weno3.py new file mode 100644 index 00000000..929061c1 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/reconstructor/weno3.py @@ -0,0 +1,59 @@ +# reconstructor/weno3.py +import numpy as np +from .base import Reconstructor +from cfd_registry import ComponentRegistry, register_component + +@register_component('reconstructor', 'weno3') +class Weno3Reconstructor(Reconstructor): + def compute_face_values(self, q, cfd): + domain = cfd.domain + solution = cfd.solution + self._reconstruct_left_interfaces(domain, q, solution.q_face_left) + self._reconstruct_right_interfaces(domain, q, solution.q_face_right) + + def _reconstruct_left_interfaces(self, domain, u, qL): + """在每个 i+1/2 界面,计算左单元贡献的 qL (即 u_{i+1/2}^-)""" + for i in range(domain.ist - 1, domain.ied): + j = i - (domain.ist - 1) + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qL[j] = self._reconstruct_from_right_biased_stencil(v3, v2, v1) + qL[j] = self._reconstruct_from_left_biased_stencil(v1, v2, v3) + + def _reconstruct_right_interfaces(self, domain, u, qR): + """在每个 i+1/2 界面,计算右单元贡献的 qR (即 u_{i+1/2}^+)""" + for i in range(domain.ist, domain.ied + 1): + j = i - domain.ist + v1, v2, v3 = u[i-1], u[i], u[i+1] + #qR[j] = self._reconstruct_from_left_biased_stencil(v3, v2, v1) + qR[j] = self._reconstruct_from_right_biased_stencil(v1, v2, v3) + + def _reconstruct_from_left_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + + def _reconstruct_from_right_biased_stencil(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + diff --git a/example/1d-linear-convection/weno3/python/06g/registry.py b/example/1d-linear-convection/weno3/python/06g/registry.py new file mode 100644 index 00000000..61be9050 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/registry.py @@ -0,0 +1,75 @@ +# registry.py (改进版) +""" +CFD组件注册系统 - 简洁高效版 +""" + +from typing import Dict, Type, Any + +class ComponentRegistry: + """组件注册表""" + + _registries: Dict[str, Dict[str, Type]] = {} + _verbose = True # 控制是否打印注册信息 + + @classmethod + def set_verbose(cls, verbose: bool): + """设置是否打印注册信息""" + cls._verbose = verbose + + @classmethod + def register(cls, category: str, name: str, component_class: Type): + """注册组件类""" + if category not in cls._registries: + cls._registries[category] = {} + + # 检查是否已注册 + if name in cls._registries[category]: + if cls._registries[category][name] == component_class: + # 相同类重复注册,静默跳过 + return + elif cls._verbose: + print(f"⚠️ 覆盖注册: {category}.{name}") + + cls._registries[category][name] = component_class + if cls._verbose: + print(f"✅ 已注册: {category}.{name} -> {component_class.__name__}") + + @classmethod + def get(cls, category: str, name: str) -> Type: + """获取组件类""" + if category not in cls._registries: + available = list(cls._registries.keys()) + raise ValueError(f"❌ 未知类别: {category} (可用类别: {available})") + + if name not in cls._registries[category]: + available = list(cls._registries[category].keys()) + raise ValueError(f"❌ 未找到: {category}.{name} (可用: {available})") + + return cls._registries[category][name] + + @classmethod + def create(cls, category: str, name: str, *args, **kwargs) -> Any: + """创建组件实例""" + component_class = cls.get(category, name) + return component_class(*args, **kwargs) + + @classmethod + def list_all(cls) -> Dict[str, list]: + """列出所有已注册的组件""" + return { + category: list(components.keys()) + for category, components in cls._registries.items() + } + + @classmethod + def get_count(cls) -> int: + """获取注册组件总数""" + return sum(len(components) for components in cls._registries.values()) + +def register_component(category: str, name: str = None): + """简化注册的装饰器""" + def decorator(cls): + component_name = name or cls.__name__.lower() + ComponentRegistry.register(category, component_name, cls) + return cls + return decorator \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06g/residual.py b/example/1d-linear-convection/weno3/python/06g/residual.py new file mode 100644 index 00000000..e0f96cd3 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/residual.py @@ -0,0 +1,40 @@ +# residual.py + +from flux.factory import FluxCalculatorFactory +from reconstructor import ReconstructorFactory + +class ResidualCalculator: + """残差计算器:封装「重建→通量→散度」完整流程""" + def __init__(self, cfd): + self.cfd = cfd + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.mesh = self.domain.mesh + + self.reconstructor = ReconstructorFactory.create(self.config, self.domain) + self.flux_calculator = FluxCalculatorFactory.create(cfd) + + def compute(self): + """计算完整残差(对外唯一接口)""" + self._compute_face_values() + self._compute_inviscid_flux() + self._compute_flux_divergence() + + def _compute_face_values(self): + """私有方法:界面值重建""" + self.reconstructor.compute_face_values(self.solution.u, self.cfd) + + def _compute_inviscid_flux(self): + """私有方法:计算无粘通量""" + self.flux_calculator.compute( + self.solution.q_face_left, + self.solution.q_face_right, + self.solution.flux + ) + + def _compute_flux_divergence(self): + """私有方法:计算通量散度(残差 = -dF/dx)""" + solution = self.solution + for i in range(self.mesh.ncells): + solution.res[i] = -(solution.flux[i+1] - solution.flux[i]) / self.mesh.dx \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06g/result.py b/example/1d-linear-convection/weno3/python/06g/result.py new file mode 100644 index 00000000..acd257b0 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/result.py @@ -0,0 +1,21 @@ +# result.py + +from initial_condition import InitialConditionFactory + +class ResultAssembler: + @staticmethod + def assemble(cfd: 'Cfd') -> dict: + u_numerical = cfd.solution.u[cfd.domain.ist:cfd.domain.ied].copy() + analytical = InitialConditionFactory.get_exact_solution(cfd) + + return { + "x": cfd.domain.mesh.xcc, + "numerical": u_numerical, + "analytical": analytical, + "config": { + "scheme": cfd.config.recon_scheme, + "order": cfd.config.spatial_order, + "rk_order": cfd.config.rk_order, + "final_time": cfd.config.final_time + } + } \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06g/run_eno_weno.py b/example/1d-linear-convection/weno3/python/06g/run_eno_weno.py new file mode 100644 index 00000000..ff46f226 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/run_eno_weno.py @@ -0,0 +1,54 @@ +# run_eno_weno.py + +from solver import Cfd +from config import CfdConfig +from mesh import Mesh +from plotter import plot_eno_weno_comparison, CFDPlotter + +def performEnoWenoAnalysis(): + # 1. 初始化网格 + #mesh = Mesh(ncells=100, L=2.0) + mesh = Mesh() + plotter = CFDPlotter() + + # 2. 配置并运行ENO3求解(使用你的链式调用) + print("Running ENO3 solver...") + config_eno3 = CfdConfig() # 初始化默认配置 + config_eno3.with_reconstruction("eno", 3) # 显式指定3阶(也可省略,ENO默认3阶) + # 可选:覆盖默认值(如dt) + config_eno3.dt = 0.0025 + config_eno3.rk_order = 2 + + cfd_eno3 = Cfd(config_eno3, mesh) + cfd_eno3.run() # 求解并生成result字典 + + # 可选:快速验证ENO3结果 + # plotter.plot_quick(cfd_eno3.result, title="ENO3 Quick Check") + + # 3. 配置并运行WENO3求解(注意:WENO默认5阶,这里显式指定3阶) + print("Running WENO3 solver...") + config_weno3 = CfdConfig() + config_weno3.with_reconstruction("weno", 3) # 显式指定3阶(默认是5阶) + # 可选:覆盖默认值 + config_weno3.dt = 0.0025 + config_weno3.rk_order = 2 + + cfd_weno3 = Cfd(config_weno3, mesh) + cfd_weno3.run() + + # 4. 可选:保存结果(供离线绘图) + # cfd_eno3.save_result("eno3_result.npz") + # cfd_weno3.save_result("weno3_result.npz") + + # 5. 绘制ENO/WENO对比图 + print("Plotting comparison results...") + plot_eno_weno_comparison( + eno_result=cfd_eno3.result, + weno_result=cfd_weno3.result, + save_path="eno_weno_comparison.png" # 可选:保存图片 + ) + +if __name__ == "__main__": + # 主程序入口 + performEnoWenoAnalysis() + print("Analysis completed!") \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06g/solution.py b/example/1d-linear-convection/weno3/python/06g/solution.py new file mode 100644 index 00000000..3d4cba56 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/solution.py @@ -0,0 +1,40 @@ +# solution.py +import numpy as np +from initial_condition import InitialConditionFactory + +class Solution: + def __init__(self, config, domain): + """ + 初始化求解过程中的动态数据 + :param domain: Domain实例(用于确定数组尺寸) + """ + self.domain = domain + mesh = domain.mesh + + # 界面值和通量(维度依赖mesh.nnodes) + self.q_face_left = np.zeros(mesh.nnodes) # 左界面值 + self.q_face_right = np.zeros(mesh.nnodes) # 右界面值 + self.flux = np.zeros(mesh.nnodes) # 通量 + + # 残差(维度依赖mesh.ncells) + self.res = np.zeros(mesh.ncells) # 残差 + + # 解数组(维度依赖ntcells,含ghost层) + self.u = np.zeros(domain.ntcells) # 当前解 + self.un = np.zeros(domain.ntcells) # 上一时间步解 + + self.initialize_from_config(config) + + def reset_solution(self): + """重置解数组为初始状态""" + self.u.fill(0.0) + self.un.fill(0.0) + + def initialize_from_config(self, config): + """根据配置初始化场""" + ic = InitialConditionFactory.create(config) + ic.apply(self) + + def update_old_field(self): + """更新旧场""" + self.un[:] = self.u[:] \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06g/solver.py b/example/1d-linear-convection/weno3/python/06g/solver.py new file mode 100644 index 00000000..6a320e26 --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/solver.py @@ -0,0 +1,42 @@ +from boundary import BoundaryConditionFactory +from initial_condition import InitialConditionFactory +from time_integration import TimeIntegrator,TimeIntegratorFactory +from mesh import Mesh +from domain import Domain +from solution import Solution +from config import CfdConfig +from result import ResultAssembler + +# Cfd class: main data structure containing all CFD data +class Cfd: + def __init__(self, config, mesh): + self.config = config + self.domain = Domain(config, mesh) + self.solution = Solution(config, self.domain) + self.integrator = TimeIntegratorFactory.create(self) + self.boundary_condition = BoundaryConditionFactory.create(self) + + def run(self): + # 应用初始边界条件并同步 old field + self.boundary_condition.apply(self.solution.u) + self.solution.update_old_field() + + t = 0.0 + dt_old = self.config.dt + dt = dt_old + + domain = self.domain + solution = self.solution + config = self.config + + while t < config.final_time: + if t + dt > config.final_time: + dt = config.final_time - t + config.dt = dt # temporary adjustment for last step + self.integrator.step(dt) + t += dt + config.dt = dt_old + + self.result = ResultAssembler.assemble(self) + return self.result["numerical"] + \ No newline at end of file diff --git a/example/1d-linear-convection/weno3/python/06g/time_integration.py b/example/1d-linear-convection/weno3/python/06g/time_integration.py new file mode 100644 index 00000000..51b2b20c --- /dev/null +++ b/example/1d-linear-convection/weno3/python/06g/time_integration.py @@ -0,0 +1,126 @@ +# time_integration.py + +""" +时间推进器模块 (已集成注册系统) +使用装饰器 @register_component 自动注册 +""" + +from abc import ABC, abstractmethod +from cfd_registry import ComponentRegistry, register_component +from residual import ResidualCalculator + +# ---------------------- 1. 抽象时间推进器基类(统一接口) ---------------------- +class TimeIntegrator(ABC): + """时间推进器抽象基类:定义一维CFD时间推进的核心接口""" + def __init__(self, cfd): + self.cfd = cfd # 持有CFD实例,获取配置/域/求解数据 + self.config = cfd.config + self.domain = cfd.domain + self.solution = cfd.solution + self.residual_calculator = ResidualCalculator(cfd) + + @abstractmethod + def step(self, dt): + """ + 单次时间步推进(核心接口) + :param dt: 时间步长 + :return: None + """ + pass + + # 公共逻辑:复用残差计算、边界条件、数组索引映射 + def compute_residual(self): + """计算残差(所有RK方法都需要,封装为公共方法)""" + self.residual_calculator.compute() + + def apply_boundary(self): + """应用边界条件(公共逻辑)""" + self.cfd.boundary_condition.apply(self.solution.u) + + def map_idx(self, i): + """物理网格索引 → 残差数组索引(公共映射逻辑)""" + return i - self.domain.ist + +# ---------------------- 2. 具体RK时间推进器实现(使用装饰器注册) ---------------------- + +@register_component('integrator', 'rk1') +class RK1Integrator(TimeIntegrator): + """1阶显式欧拉(RK1)""" + def step(self, dt): + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] += dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk2') +class RK2Integrator(TimeIntegrator): + """2阶Heun方法(RK2)""" + def step(self, dt): + # 阶段1:预测步 + self.compute_residual() + u_pred = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u_pred[i] += dt * self.solution.res[j] + self.solution.u[:] = u_pred + self.apply_boundary() + + # 阶段2:校正步 + self.compute_residual() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = 0.5 * self.solution.un[i] + 0.5 * self.solution.u[i] + 0.5 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +@register_component('integrator', 'rk3') +class RK3Integrator(TimeIntegrator): + """3阶SSPRK3(强稳定保号RK3)""" + def step(self, dt): + # 阶段1 + self.compute_residual() + u1 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u1[i] += dt * self.solution.res[j] + self.solution.u[:] = u1 + self.apply_boundary() + + # 阶段2 + self.compute_residual() + u2 = self.solution.u.copy() + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + u2[i] = 0.75 * self.solution.un[i] + 0.25 * self.solution.u[i] + 0.25 * dt * self.solution.res[j] + self.solution.u[:] = u2 + self.apply_boundary() + + # 阶段3 + self.compute_residual() + c1, c2, c3 = 1.0/3.0, 2.0/3.0, 2.0/3.0 + for i in range(self.domain.ist, self.domain.ied): + j = self.map_idx(i) + self.solution.u[i] = c1 * self.solution.un[i] + c2 * self.solution.u[i] + c3 * dt * self.solution.res[j] + self.apply_boundary() + self.solution.update_old_field() + +class TimeIntegratorFactory: + """时间推进器工厂""" + + @staticmethod + def create(cfd) -> 'TimeIntegrator': + """ + 创建时间推进器实例 + + Args: + cfd: CFD对象 + + Returns: + 时间推进器实例 + """ + from factories.base_factory import BaseFactory + rk_order = cfd.config.rk_order + integrator_name = f'rk{rk_order}' + return BaseFactory.create_component('integrator', integrator_name, cfd) \ No newline at end of file diff --git a/example/codegen/weno_reconstructor/01/eno_weno_coefficients.yaml b/example/codegen/weno_reconstructor/01/eno_weno_coefficients.yaml new file mode 100644 index 00000000..10219b2a --- /dev/null +++ b/example/codegen/weno_reconstructor/01/eno_weno_coefficients.yaml @@ -0,0 +1,75 @@ +# ENO reconstruction coefficients + +eno: + 1: + - [ 1] # r = 0 + - [ 1] # r = -1 + + 2: + - [ -1/2, 3/2] # r = 1 + - [ 1/2, 1/2] # r = 0 + - [ 3/2, -1/2] # r = -1 + + 3: + - [ 1/3, -7/6, 11/6] # r = 2 + - [ -1/6, 5/6, 1/3] # r = 1 + - [ 1/3, 5/6, -1/6] # r = 0 + - [ 11/6, -7/6, 1/3] # r = -1 + + 4: + - [ -1/4, 13/12, -23/12, 25/12] # r = 3 + - [ 1/12, -5/12, 13/12, 1/4] # r = 2 + - [ -1/12, 7/12, 7/12, -1/12] # r = 1 + - [ 1/4, 13/12, -5/12, 1/12] # r = 0 + - [ 25/12, -23/12, 13/12, -1/4] # r = -1 + + 5: + - [ 1/5, -21/20, 137/60, -163/60, 137/60] # r = 4 + - [ -1/20, 17/60, -43/60, 77/60, 1/5] # r = 3 + - [ 1/30, -13/60, 47/60, 9/20, -1/20] # r = 2 + - [ -1/20, 9/20, 47/60, -13/60, 1/30] # r = 1 + - [ 1/5, 77/60, -43/60, 17/60, -1/20] # r = 0 + - [ 137/60, -163/60, 137/60, -21/20, 1/5] # r = -1 + + 6: + - [ -1/6, 31/30, -163/60, 79/20, -71/20, 49/20] # r = 5 + - [ 1/30, -13/60, 37/60, -21/20, 29/20, 1/6] # r = 4 + - [ -1/60, 7/60, -23/60, 19/20, 11/30, -1/30] # r = 3 + - [ 1/60, -2/15, 37/60, 37/60, -2/15, 1/60] # r = 2 + - [ -1/30, 11/30, 19/20, -23/60, 7/60, -1/60] # r = 1 + - [ 1/6, 29/20, -21/20, 37/60, -13/60, 1/30] # r = 0 + - [ 49/20, -71/20, 79/20, -163/60, 31/30, -1/6] # r = -1 + + 7: + - [ 1/7, -43/42, 667/210, -2341/420, 853/140, -617/140, 363/140] # r = 6 + - [ -1/42, 37/210, -241/420, 153/140, -197/140, 223/140, 1/7] # r = 5 + - [ 1/105, -31/420, 109/420, -241/420, 153/140, 13/42, -1/42] # r = 4 + - [ -1/140, 5/84, -101/420, 319/420, 107/210, -19/210, 1/105] # r = 3 + - [ 1/105, -19/210, 107/210, 319/420, -101/420, 5/84, -1/140] # r = 2 + - [ -1/42, 13/42, 153/140, -241/420, 109/420, -31/420, 1/105] # r = 1 + - [ 1/7, 223/140, -197/140, 153/140, -241/420, 37/210, -1/42] # r = 0 + - [ 363/140, -617/140, 853/140, -2341/420, 667/210, -43/42, 1/7] # r = -1 + +weno_schemes: + 1: # 1st-order WENO (trivial) + inputs: [v1] # v1=u_i + betas: + beta0: "0" # r=1 + weights: + d: [1] + 2: # 3rd-order WENO (k=2 → 2k-1=3) + inputs: [v1, v2, v3] # v1=u_im1, v2=u_i, v3=u_ip1 + betas: + beta0: "(v2 - v1)**2" # r=1 + beta1: "(v3 - v2)**2" + weights: + d: [1/3, 2/3] + 3: # 5th-order WENO (k=3 → 2k-1=5) + inputs: [v1, v2, v3, v4, v5] # v1=u_im2, v2=u_im1, v3=u_i, v4=u_ip1, v5=u_ip2 + betas: + beta0: "(13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2" # r=2 + beta1: "(13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2" # r=1 + beta2: "(13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2" # r=0 + weights: + d: [1/10, 3/5, 3/10] + diff --git a/example/codegen/weno_reconstructor/01/generate_weno_coef.py b/example/codegen/weno_reconstructor/01/generate_weno_coef.py new file mode 100644 index 00000000..452d4f7e --- /dev/null +++ b/example/codegen/weno_reconstructor/01/generate_weno_coef.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import yaml +from pathlib import Path +import sys + +def fraction_str_to_code(frac) -> str: + """Convert '3/2' or 1 to '3.0/2.0' or '1.0'.""" + if isinstance(frac, str): + if '/' in frac: + a, b = frac.split('/') + return f"{float(a):g}.0/{float(b):g}.0" + else: + return f"{float(frac):g}.0" + else: + # Handle int/float from YAML + return f"{float(frac):g}.0" + +def build_equation_str(idx, coeffs, fraction_converter): + """ + 构建格式正确的方程字符串(避免多余的+号) + + 参数: + idx: 当前索引(用于计算变量名v的编号) + coeffs: 当前索引对应的系数列表(字符串形式) + fraction_converter: 分数字符串转代码格式的函数 + + 返回: + 格式化后的方程右侧字符串(如 "1/2*v1-1/3*v2+2/5*v3") + """ + # 转换系数格式 + code_coeffs = [fraction_converter(c) for c in coeffs] + + # 生成带正确符号的项 + terms = [] + for i in range(len(code_coeffs)): + coeff = code_coeffs[i] + var_name = f"v{idx + i + 1}" # 计算变量名(v1/v2/v3...) + + if coeff.startswith('-'): + # 负数项:直接拼接,保留负号 + terms.append(f"{coeff}*{var_name}") + else: + # 正数项:添加+号(第一项后续处理) + terms.append(f"+{coeff}*{var_name}") + + # 处理第一项的多余+号 + if terms and terms[0].startswith('+'): + terms[0] = terms[0][1:] + + # 拼接最终表达式 + return "".join(terms) + +def generate_weno_function_code(func_name: str, yaml_path: str, weno_order: int, lr: int) -> list: + """Generate Python function code from YAML coefficient file.""" + with open(yaml_path, 'r', encoding='utf-8') as f: + data = yaml.safe_load(f) + + if not data: + raise ValueError(f"YAML file {yaml_path} is empty") + + + eno_order = weno_order // 2 + 1 + print(f"weno_order,eno_order={weno_order,eno_order}") + + k = eno_order # spatial order = k + + scheme = data["weno_schemes"][k] + print(f"scheme={scheme}") + + inputs = scheme["inputs"] + input_str = ", ".join(inputs) + + lines = [] + lines.append(f"def {func_name}(self, {input_str}):") + + is_reversed = False + if not is_reversed: + # r = k-1, k-2, ..., 0, -1 + r_vals = list(range(k - 1, -2, -1)) + else: + # r = -1, 0, 1, ..., k-1 + r_vals = list(range(-1, k)) + + + print(f"r_vals={r_vals}") + lines.append(f" eps = 1e-6") + + + # Compute betas + betas = scheme["betas"] + for name, expr in betas.items(): + lines.append(f" {name} = {expr}") + + # Linear weights + weights = scheme["weights"]["d"] + weights_iter = reversed(weights) if lr == 1 else weights + + for i, d in enumerate(weights_iter): + d_code = fraction_str_to_code(d) + lines.append(f" d{i} = {d_code}") + + for idx in range( eno_order ): + lines.append(f" alpha{idx} = d{idx} / (eps + beta{idx})**2") + + alpha = " + ".join(f"alpha{i}" for i in range(eno_order) ) + + lines.append(f" alpha = {alpha}") + for idx in range( eno_order ): + lines.append(f" w{idx} = alpha{idx} / alpha") + + # 获取 ENO 系数 + eno_data = data["eno"] # ENO + coeffs_list = eno_data[eno_order] + + ishift = 1 if lr == 1 else 0 + + for idx in range( eno_order ): + coeffs = coeffs_list[idx+ishift] + + code_coeffs = [fraction_str_to_code(c) for c in coeffs] + #print(f"idx,coeffs={idx,coeffs}") + + result = "+".join(f"{code_coeffs[i]}*v{idx+i+1}" for i in range(len(coeffs))) + + equation_right = build_equation_str(idx, coeffs, fraction_str_to_code) + #print(f"q{idx} = {result}") + print(f"q{idx} = {equation_right}") + r = r_vals[idx] + + lines.append(f" q{idx} = {equation_right} # r={r}") + + f = " + ".join(f"w{i} * q{i}" for i in range(eno_order) ) + lines.append(f" return {f}") + + return lines + +def main(): + input_yaml = "eno_weno_coefficients.yaml" + output_file = "weno_reconstructors.py" + + weno_order = 3 + + # Write combined output + full_lines = [ + "# Auto-generated WENO coefficient initializers", + "# DO NOT EDIT MANUALLY", + "" + ] + + for weno_order in [1,3,5]: + left_lines = generate_weno_function_code( + f"_reconstruct_weno{weno_order}_left", + input_yaml, + weno_order=weno_order, + lr=-1 + ) + + right_lines = generate_weno_function_code( + f"_reconstruct_weno{weno_order}_right", + input_yaml, + weno_order=weno_order, + lr=1 + ) + + full_lines.extend(left_lines) + full_lines.append("") + full_lines.extend(right_lines) + full_lines.append("") + + with open(output_file, 'w', encoding='utf-8') as f: + f.write("\n".join(full_lines)) + + print(f"Generated {output_file} from:") + print(f" - {input_yaml}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/example/codegen/weno_reconstructor/01/weno_reconstructors.py b/example/codegen/weno_reconstructor/01/weno_reconstructors.py new file mode 100644 index 00000000..15a9972e --- /dev/null +++ b/example/codegen/weno_reconstructor/01/weno_reconstructors.py @@ -0,0 +1,92 @@ +# Auto-generated WENO coefficient initializers +# DO NOT EDIT MANUALLY + +def _reconstruct_weno1_left(self, v1): + eps = 1e-6 + beta0 = 0 + d0 = 1.0 + alpha0 = d0 / (eps + beta0)**2 + alpha = alpha0 + w0 = alpha0 / alpha + q0 = 1.0*v1 # r=0 + return w0 * q0 + +def _reconstruct_weno1_right(self, v1): + eps = 1e-6 + beta0 = 0 + d0 = 1.0 + alpha0 = d0 / (eps + beta0)**2 + alpha = alpha0 + w0 = alpha0 / alpha + q0 = 1.0*v1 # r=0 + return w0 * q0 + +def _reconstruct_weno3_left(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 1.0/3.0 + d1 = 2.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = -1.0/2.0*v1+3.0/2.0*v2 # r=1 + q1 = 1.0/2.0*v2+1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + +def _reconstruct_weno3_right(self, v1, v2, v3): + eps = 1e-6 + beta0 = (v2 - v1)**2 + beta1 = (v3 - v2)**2 + d0 = 2.0/3.0 + d1 = 1.0/3.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha = alpha0 + alpha1 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + q0 = 1.0/2.0*v1+1.0/2.0*v2 # r=1 + q1 = 3.0/2.0*v2-1.0/2.0*v3 # r=0 + return w0 * q0 + w1 * q1 + +def _reconstruct_weno5_left(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 1.0/10.0 + d1 = 3.0/5.0 + d2 = 3.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = 1.0/3.0*v1-7.0/6.0*v2+11.0/6.0*v3 # r=2 + q1 = -1.0/6.0*v2+5.0/6.0*v3+1.0/3.0*v4 # r=1 + q2 = 1.0/3.0*v3+5.0/6.0*v4-1.0/6.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 + +def _reconstruct_weno5_right(self, v1, v2, v3, v4, v5): + eps = 1e-6 + beta0 = (13.0/12.0)*(v1 - 2*v2 + v3)**2 + (1.0/4.0)*(v1 - 4*v2 + 3*v3)**2 + beta1 = (13.0/12.0)*(v2 - 2*v3 + v4)**2 + (1.0/4.0)*(v2 - v4)**2 + beta2 = (13.0/12.0)*(v3 - 2*v4 + v5)**2 + (1.0/4.0)*(3*v3 - 4*v4 + v5)**2 + d0 = 3.0/10.0 + d1 = 3.0/5.0 + d2 = 1.0/10.0 + alpha0 = d0 / (eps + beta0)**2 + alpha1 = d1 / (eps + beta1)**2 + alpha2 = d2 / (eps + beta2)**2 + alpha = alpha0 + alpha1 + alpha2 + w0 = alpha0 / alpha + w1 = alpha1 / alpha + w2 = alpha2 / alpha + q0 = -1.0/6.0*v1+5.0/6.0*v2+1.0/3.0*v3 # r=2 + q1 = 1.0/3.0*v2+5.0/6.0*v3-1.0/6.0*v4 # r=1 + q2 = 11.0/6.0*v3-7.0/6.0*v4+1.0/3.0*v5 # r=0 + return w0 * q0 + w1 * q1 + w2 * q2 diff --git a/modern-cfd/src/cgns/include/Cgns_t.h b/modern-cfd/src/cgns/include/Cgns_t.h index 329bc985..e77cf3c0 100644 --- a/modern-cfd/src/cgns/include/Cgns_t.h +++ b/modern-cfd/src/cgns/include/Cgns_t.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/cgns/include/cgnstest.h b/modern-cfd/src/cgns/include/cgnstest.h index 6e793a1a..b7bcf9d5 100644 --- a/modern-cfd/src/cgns/include/cgnstest.h +++ b/modern-cfd/src/cgns/include/cgnstest.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/cgns/src/Cgns_t.cpp b/modern-cfd/src/cgns/src/Cgns_t.cpp index fd02fdeb..4d7596c7 100644 --- a/modern-cfd/src/cgns/src/Cgns_t.cpp +++ b/modern-cfd/src/cgns/src/Cgns_t.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/cgns/src/cgnstest.cpp b/modern-cfd/src/cgns/src/cgnstest.cpp index 3c0c1f6a..17254090 100644 --- a/modern-cfd/src/cgns/src/cgnstest.cpp +++ b/modern-cfd/src/cgns/src/cgnstest.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/geometry/include/Geom.h b/modern-cfd/src/geometry/include/Geom.h index e3ee2422..6439008a 100644 --- a/modern-cfd/src/geometry/include/Geom.h +++ b/modern-cfd/src/geometry/include/Geom.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/geometry/include/Grid.h b/modern-cfd/src/geometry/include/Grid.h index e3311da7..4549920a 100644 --- a/modern-cfd/src/geometry/include/Grid.h +++ b/modern-cfd/src/geometry/include/Grid.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/geometry/src/Geom.cpp b/modern-cfd/src/geometry/src/Geom.cpp index 316766dd..7d07d85d 100644 --- a/modern-cfd/src/geometry/src/Geom.cpp +++ b/modern-cfd/src/geometry/src/Geom.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/geometry/src/Grid.cpp b/modern-cfd/src/geometry/src/Grid.cpp index e4fafad2..0e78f0f1 100644 --- a/modern-cfd/src/geometry/src/Grid.cpp +++ b/modern-cfd/src/geometry/src/Grid.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/main/include/Simu.h b/modern-cfd/src/main/include/Simu.h index 490ccfab..134af7bd 100644 --- a/modern-cfd/src/main/include/Simu.h +++ b/modern-cfd/src/main/include/Simu.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/main/src/Simu.cpp b/modern-cfd/src/main/src/Simu.cpp index c7e91250..aac41900 100644 --- a/modern-cfd/src/main/src/Simu.cpp +++ b/modern-cfd/src/main/src/Simu.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/main/src/main.cpp b/modern-cfd/src/main/src/main.cpp index 504554e4..61179b95 100644 --- a/modern-cfd/src/main/src/main.cpp +++ b/modern-cfd/src/main/src/main.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/parallel/include/Cmpi.h b/modern-cfd/src/parallel/include/Cmpi.h index 642b7269..48eb9aff 100644 --- a/modern-cfd/src/parallel/include/Cmpi.h +++ b/modern-cfd/src/parallel/include/Cmpi.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/parallel/src/Cmpi.cpp b/modern-cfd/src/parallel/src/Cmpi.cpp index 6772b2ad..efda4d1d 100644 --- a/modern-cfd/src/parallel/src/Cmpi.cpp +++ b/modern-cfd/src/parallel/src/Cmpi.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/project/include/Project.h b/modern-cfd/src/project/include/Project.h index 918ea936..82d5a6b8 100644 --- a/modern-cfd/src/project/include/Project.h +++ b/modern-cfd/src/project/include/Project.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/project/src/Project.cpp b/modern-cfd/src/project/src/Project.cpp index 16606d34..ceea839c 100644 --- a/modern-cfd/src/project/src/Project.cpp +++ b/modern-cfd/src/project/src/Project.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/solver/include/CfdPara.h b/modern-cfd/src/solver/include/CfdPara.h index 0a4eb16d..6ba06a87 100644 --- a/modern-cfd/src/solver/include/CfdPara.h +++ b/modern-cfd/src/solver/include/CfdPara.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/solver/include/Solver.h b/modern-cfd/src/solver/include/Solver.h index f554c2bc..a4582f21 100644 --- a/modern-cfd/src/solver/include/Solver.h +++ b/modern-cfd/src/solver/include/Solver.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/solver/include/SolverDetail.h b/modern-cfd/src/solver/include/SolverDetail.h index 55e47804..b524173e 100644 --- a/modern-cfd/src/solver/include/SolverDetail.h +++ b/modern-cfd/src/solver/include/SolverDetail.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/solver/include/SolverDetailCpu.h b/modern-cfd/src/solver/include/SolverDetailCpu.h index d983d4f2..433e81ae 100644 --- a/modern-cfd/src/solver/include/SolverDetailCpu.h +++ b/modern-cfd/src/solver/include/SolverDetailCpu.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/solver/include/SolverDetailCuda.h b/modern-cfd/src/solver/include/SolverDetailCuda.h index ec4e9887..f17c319b 100644 --- a/modern-cfd/src/solver/include/SolverDetailCuda.h +++ b/modern-cfd/src/solver/include/SolverDetailCuda.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/solver/src/CfdPara.cpp b/modern-cfd/src/solver/src/CfdPara.cpp index c3462160..bc19dbd6 100644 --- a/modern-cfd/src/solver/src/CfdPara.cpp +++ b/modern-cfd/src/solver/src/CfdPara.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/solver/src/Solver.cpp b/modern-cfd/src/solver/src/Solver.cpp index 32412bb6..e6150113 100644 --- a/modern-cfd/src/solver/src/Solver.cpp +++ b/modern-cfd/src/solver/src/Solver.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/solver/src/SolverDetail.cpp b/modern-cfd/src/solver/src/SolverDetail.cpp index fccf4bf4..a1107327 100644 --- a/modern-cfd/src/solver/src/SolverDetail.cpp +++ b/modern-cfd/src/solver/src/SolverDetail.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/solver/src/SolverDetailCpu.cpp b/modern-cfd/src/solver/src/SolverDetailCpu.cpp index 8b8e5984..be5e522b 100644 --- a/modern-cfd/src/solver/src/SolverDetailCpu.cpp +++ b/modern-cfd/src/solver/src/SolverDetailCpu.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/tools/include/tools.h b/modern-cfd/src/tools/include/tools.h index 83e15c34..8e03adcd 100644 --- a/modern-cfd/src/tools/include/tools.h +++ b/modern-cfd/src/tools/include/tools.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/visualize/include/Visual.h b/modern-cfd/src/visualize/include/Visual.h index 2a0c8d9a..bfc9adff 100644 --- a/modern-cfd/src/visualize/include/Visual.h +++ b/modern-cfd/src/visualize/include/Visual.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/src/visualize/src/Visual.cpp b/modern-cfd/src/visualize/src/Visual.cpp index 7dab32cd..cd47f04e 100644 --- a/modern-cfd/src/visualize/src/Visual.cpp +++ b/modern-cfd/src/visualize/src/Visual.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/ui/MyDataBase.cpp b/modern-cfd/ui/MyDataBase.cpp index 4b47a281..cc23dbce 100644 --- a/modern-cfd/ui/MyDataBase.cpp +++ b/modern-cfd/ui/MyDataBase.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/ui/MyDataBase.h b/modern-cfd/ui/MyDataBase.h index 156f92f3..1e1160bb 100644 --- a/modern-cfd/ui/MyDataBase.h +++ b/modern-cfd/ui/MyDataBase.h @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/ui/main.cpp b/modern-cfd/ui/main.cpp index 7f68aab8..c5f553d9 100644 --- a/modern-cfd/ui/main.cpp +++ b/modern-cfd/ui/main.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. diff --git a/modern-cfd/ui/mainwindow.cpp b/modern-cfd/ui/mainwindow.cpp index 605a2be1..711eec1c 100644 --- a/modern-cfd/ui/mainwindow.cpp +++ b/modern-cfd/ui/mainwindow.cpp @@ -1,6 +1,6 @@ /*---------------------------------------------------------------------------*\ OneFLOW - LargeScale Multiphysics Scientific Simulation Environment -Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. +Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW. @@ -119,7 +119,7 @@ void MainWindow::initMenu() this->treeWidget->setGeometry(QRect(100, 50, 600, 300)); this->treeWidget->setColumnCount( 1 ); QStringList labels; - labels << QString::fromLocal8Bit("CFD"); + labels << QString::fromLocal8Bit("CFD参数"); this->treeWidget->setHeaderLabels( labels ); this->treeWidget->header()->setSectionResizeMode(QHeaderView::Stretch); this->treeWidget->setContextMenuPolicy(Qt::CustomContextMenu); diff --git a/test/test.py b/test/test.py index 2b1df9e5..382f30e2 100644 --- a/test/test.py +++ b/test/test.py @@ -2,7 +2,7 @@ ''' --------------------------------------------------------------------------- OneFLOW - LargeScale Multiphysics Scientific Simulation Environment - Copyright (C) 2017-2025 He Xin and the OneFLOW contributors. + Copyright (C) 2017-2026 He Xin and the OneFLOW contributors. ------------------------------------------------------------------------------- License This file is part of OneFLOW.